diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..cdec8a81 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,26 @@ +# Default behavior: if Git thinks a file is text (as opposed to binary), it +# will normalize line endings to LF in the repository, but convert to your +# platform's native line endings on checkout (e.g., CRLF for Windows). +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. E.g., +#*.c text + +# Declare files that will always have CRLF line endings on checkout. E.g., +#*.sln text eol=crlf + +# Declare files that will always have LF line endings on checkout. E.g., +*.sh text eol=lf + +# Denote all files that should not have line endings normalized, should not be +# merged, and should not show in a textual diff. +*.docm binary +*.docx binary +*.ico binary +*.lib binary +*.png binary +*.pptx binary +*.snk binary +*.vsdx binary +*.xps binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..c6e1680e --- /dev/null +++ b/.gitignore @@ -0,0 +1,219 @@ +# Compiled object files +*.o +*.opp + +# Compiled static libraries +*.a + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml + +# NuGet Packages Directory +packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + + +#LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac desktop service store files +.DS_Store + +# Visual studio build artifacts +*.tlog +*.lastbuildstate +*.idb +*.exp +*.lib +*.dll + +# Windows CE build artifacts +Build.err +Build.wrn +Buildx86retail.dat +*.dat + +# Tools EXE that doesn't end up in a typical build directory +c/common/tools/macro_utils_h_generator/macro_utils_h_generator.exe + +# hg directories should be ignored +**/hg/ + +# Java +java/**/.idea/ +java/**/out/ +java/**/target/ +java/**/*.iml +java/**/*dependency-reduced-pom.xml +tools/jenkins-cli.jar + +# Node.js +**/.settings/ +**/node_modules/ +**/.idea/ + +# VS Code stuff +typings/** +.vscode/** + +csharp/NuGet/*.exe +csharp/NuGet/*.nupkg +csharp/.vs/* +service/java/.idea +service/java/target +service/java/iot.service.sdk.java.iml + +# ignore cmake build folder +.cmake/** +build/** + +# ignore Atom Editor files +.atom-build.json +.remote-sync.json + +# ignore VS Code C++ files +browse.VC.db + +# api reference docstates +api_reference/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..2b7c50a7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "deps/azure-c-shared-utility"] + path = deps/azure-c-shared-utility + url = https://github.com/Azure/azure-c-shared-utility +[submodule "deps/azure-iot-sdks"] + path = deps/azure-iot-sdks + url = https://github.com/azure/azure-iot-sdks +[submodule "deps/parson"] + path = deps/parson + url = https://github.com/kgabis/parson diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..1ad451d8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,98 @@ +cmake_minimum_required(VERSION 2.8.11) +project(azure_iot_gateway_sdk) + +#the following variables are project-wide and can be used with cmake-gui +option(run_e2e_tests "set run_e2e_tests to ON to run e2e tests (default is OFF) [if possible, they are always build]" OFF) +option(install_executables "should cmake run cmake's install function (that includes dynamic link libraries) [it does for yocto]" OFF) + +option(run_as_a_service "Flags that we have the goal of running gateway as a service for samples and OS that supports it." OFF) + +#making a nice global variable to know if we are on linux or not. +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(LINUX TRUE) + find_program( MEMORYCHECK_COMMAND valgrind ) + set( MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full --suppressions=${CMAKE_CURRENT_LIST_DIR}/core/valgrind_suppressions.txt --error-exitcode=1" ) +endif() + +include (CTest) + +#setting #defines +if(WIN32) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_compile_options(/guard:cf) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /guard:cf") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /guard:cf") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /guard:cf") +endif() + +if(${LINUX}) + set (CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}") +endif() + +function(add_module_to_solution undecoratedModuleName) + set_target_properties(${undecoratedModuleName} + PROPERTIES FOLDER "Modules/${undecoratedModuleName}") + set_target_properties(${undecoratedModuleName}_static + PROPERTIES FOLDER "Modules/${undecoratedModuleName}") + set_target_properties(${undecoratedModuleName}_hl + PROPERTIES FOLDER "Modules/${undecoratedModuleName}") + set_target_properties(${undecoratedModuleName}_hl_static + PROPERTIES FOLDER "Modules/${undecoratedModuleName}") +endfunction() + +function(add_sample_to_solution sampleName) + set_target_properties(${sampleName} PROPERTIES FOLDER "AzureIoTGateway_Samples") +endfunction() + + +macro(compileAsC99) + if (CMAKE_VERSION VERSION_LESS "3.1") + if (CMAKE_C_COMPILER_ID STREQUAL "GNU") + set (CMAKE_C_FLAGS "--std=c99 ${CMAKE_C_FLAGS}") + set (CMAKE_CXX_FLAGS "--std=c++11 ${CMAKE_CXX_FLAGS}") + endif() + else() + set (CMAKE_C_STANDARD 99) + set (CMAKE_CXX_STANDARD 11) + endif() +endmacro(compileAsC99) + +macro(compileAsC11) + if (CMAKE_VERSION VERSION_LESS "3.1") + if (CMAKE_C_COMPILER_ID STREQUAL "GNU") + set (CMAKE_C_FLAGS "--std=c11 ${CMAKE_C_FLAGS}") + set (CMAKE_C_FLAGS "-D_POSIX_C_SOURCE=200112L ${CMAKE_C_FLAGS}") + set (CMAKE_CXX_FLAGS "--std=c++11 ${CMAKE_CXX_FLAGS}") + endif() + else() + set (CMAKE_C_STANDARD 11) + set (CMAKE_CXX_STANDARD 11) + endif() +endmacro(compileAsC11) + +enable_testing() +compileAsC99() + +#this adds the C shared utility +add_subdirectory(./deps/azure-c-shared-utility) + +#this adds the C IoTHubClient +add_subdirectory(./deps/azure-iot-sdks/c) + +#this makes available from now on the includes of azure-c-shared-utility +include_directories(${SHARED_UTIL_INC_FOLDER}) + +function(linkSharedUtil whatIsBuilding) + target_link_libraries(${whatIsBuilding} aziotsharedutil) +endfunction(linkSharedUtil) + +#this adds parson +include_directories(./deps/parson) + +add_subdirectory(./core) + +include_directories(${GW_INC}) + +add_subdirectory(modules) + +add_subdirectory(samples) \ No newline at end of file diff --git a/License.txt b/License.txt new file mode 100644 index 00000000..7c8bfa17 --- /dev/null +++ b/License.txt @@ -0,0 +1,14 @@ +Microsoft Azure IoT Gateway SDK +Copyright (c) Microsoft Corporation + +MIT license +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..ff2f4845 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# Beta Azure IoT Gateway SDK - Readme +This repository contains the infrastructure and modules to create IoT gateway solutions. The SDK can be extended to create gateways tailored to any end to end scenario. + +Visit http://azure.com/iotdev to learn more about developing applications for Azure IoT. + + +## Operating system compatibility +The SDK is designed to be used with a broad range of operating system platforms. The Beta version of the Azure IoT Gateway SDK has been tested by means of development on the following operating systems: + +- Ubuntu 14.04 +- Ubuntu 15.10 +- Yocto Linux 3.0 on Intel Edison +- Windows 10 + +## Hardware compatibility +The SDK is designed to be independent from hardware in addition to the operating system. Developers can power their gateways with hardware as constrained as a microcontroller to systems as powerful as a ruggedized server. + +## Directory structure + +### /doc +This folder contains general documentation for the SDK as well as step by step instructions for building and running the samples: + +General documentation + +- [Dev box setup](doc/devbox_setup.md) (devbox_setup.md) contains instructions for configure your machine to build the Azure IoT Gateway SDK. +- [Getting started](doc/getting_started.md) (getting_started.md) contains an introduction to the Azure IoT Gateway SDK as well as a detailed walkthrough of the [Hello World sample](doc/sample_helloworld.md). + +Samples + +- [Hello World sample](doc/sample_helloworld.md) (sample_hello_world.md) contains step by step instructions for building and running the Hello World sample. +- [Simulated Device](doc/sample_simulated_device.md) (sample_simulated_device.md) contains step by step instructions for building and running the Simulated Device sample. +- [TI Sensor Tag](doc/sample_sensor_tag.md) (sample_sensor_tag.md) contains step by step instructions for building and running the TI Sensor Tag sample. + + +API documentation can be found at http://azure.github.io/azure-iot-gateway-sdk/api_reference/c. + +### /samples +This folder contains all of the samples for the Azure IoT Gateway SDK. Samples are seperated in their own folders. Step by step instructions for building and running each sample can be found in a sample specific .md file located in azure-iot-gateway-sdk/doc. + +### /modules +This folder contains all of the modules included with the Azure IoT Gateway SDK. Each module represents a specific piece of functionality that can be composed into an end to end gateway solution. API documentation for the modules that ship as part of the Azure IoT Gateway SDK can be found [here](whttp://azure.github.io/azure-iot-gateway-sdk). Details on the implementation of each module can be found in each module's devdoc/ folder. + +### /core +This folder contains all of the core infrastructure necessary to create a gateway solution. In general, developers only need to use components in the core folder, not modify them. API documentation for core infrastructure can be found [here](www.UPDATE_LINK.com). Details on the implementation of core components can be found in the [core/devdoc](core/devdoc). + +### /build +This is the default folder that cmake will place the output from our build scripts. The developer always has the final say about the destinaiton of build output by creating a folder, navigating to it, and then running cmake from there. Detailed instructions are contained in each sample doc (doc/sample_samplename.md). \ No newline at end of file diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt new file mode 100644 index 00000000..6e5277db --- /dev/null +++ b/ThirdPartyNotices.txt @@ -0,0 +1,144 @@ +Third Party Notices for the Azure IoT Gateway SDK Project +This Microsoft Open Source project is based on or incorporates material from the project(s) listed below (�Third Party OSS�). The original copyright notice and the license under which Microsoft received such Third Party OSS, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft licenses the Third Party OSS to you under the licensing terms for the Microsoft product or service. Microsoft reserves all other rights not expressly granted, whether by implication, estoppel or otherwise. +#SensorTag (http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User's_Guide#Data) +Creative Commons Attribution-ShareAlike 3.0 +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. +1. Definitions +a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. +b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License. +c. "Creative Commons Compatible License" means a license that is listed at https://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License. +d. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. +e. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike. +f. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. +g. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. +h. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. +i. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. +j. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. +k. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. +2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. +3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: +a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; +b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; +c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, +d. to Distribute and Publicly Perform Adaptations. +e. For the avoidance of doubt: +i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; +ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, +iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. +The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. +4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: +a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested. +b. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. +c. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. +d. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. +5. Representations, Warranties and Disclaimer +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +7. Termination +a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. +b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. +8. Miscellaneous +a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. +b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. +c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. +d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. +e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. +f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. +This product incorporates copyrighted material from the open source projects listed below (Third Party IP). The license terms of Azure IoT Gateway SDK Project do not apply to the Third Party IP which is licensed to you under its original license terms which are provided below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. You may find a copy of the Corresponding Source code, if and as required under the Third Party IP License, at http://3rdpartysource.microsoft.com. You may also obtain a copy of the source code for a period of three years after our last shipment of this product, if and as required under the Third Party IP license, by sending a check or money order for US$5.00 to: +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 USA + +Please write �source for [Third Party IP]� in the memo line of your payment. + ------------------------- +#GDBus-Glib D-Bus (https://developer.gnome.org/glib/stable/glib.html) +GNU Lesser General Public License +Version 2.1, February 1999 +Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. +[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] +Preamble +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. +This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. +When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. +To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. +Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. +When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. +We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. +For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. +In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". +A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. +The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) +"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: +a) The modified work must itself be a software library. +b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. +c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. +d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. +(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. +In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. +This option is useful when you wish to copy part of the code of the Library into a program that is not a library. +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. +However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. +When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. +If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. +You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: +a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) +b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. +c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. +d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. +e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. +For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: +a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. +b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. +NO WARRANTY +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +END OF TERMS AND CONDITIONS +How to Apply These Terms to Your New Libraries +If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). +To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + Copyright (C) +This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. +This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Also add information on how to contact you by electronic and paper mail. +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: +Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice +That's all there is to it! + diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt new file mode 100644 index 00000000..d77a3d8d --- /dev/null +++ b/core/CMakeLists.txt @@ -0,0 +1,77 @@ +#this is CMakeLists.txt for the gateway core +cmake_minimum_required(VERSION 2.8.11) + +add_subdirectory(deps) + +#this sets a global var that stores where the includes for GW are +set(GW_INC ${CMAKE_CURRENT_LIST_DIR}/inc CACHE INTERNAL "Needs to be included for gateway includes" FORCE) +set(GW_SRC ${CMAKE_CURRENT_LIST_DIR}/src CACHE INTERNAL "Needs to be included for gateway sources" FORCE) + +#setting the dynamic_loader file based on OS that it is used +if(WIN32) + include_directories(${GW_INC}/windows ) +elseif(LINUX) + include_directories(${GW_INC}/linux) +endif() + +#setting the dynamic_loader file based on OS that it is used +if(WIN32) + set(dynamic_library_c_file ./adapters/dynamic_library_windows.c ./adapters/gb_library_windows.c) +elseif(LINUX) + set(dynamic_library_c_file ./adapters/dynamic_library_linux.c ./adapters/gb_library_linux.c ) +endif() + +#setting specific libraries to be loaded based on OS (for example, Linux needs "-ldl", windows does not) +if(LINUX) + set(dynamic_loader_library dl) +endif() + +function(linkHttp whatExecutableIsBuilding) + includeHttp() + if(WIN32) + if(WINCE) + target_link_libraries(${whatExecutableIsBuilding} crypt32.lib) + target_link_libraries(${whatExecutableIsBuilding} ws2.lib) + else() + target_link_libraries(${whatExecutableIsBuilding} winhttp.lib) + endif() + else() + target_link_libraries(${whatExecutableIsBuilding} curl) + endif() +endfunction(linkHttp) + +function(includeHttp) +#function body to be similar to includeProton +endfunction(includeHttp) + + +set(gateway_c_sources + ./src/message.c + ./src/module_loader.c + ./src/message_bus.c + ./src/gateway_ll.c + ./src/gateway.c + ${dynamic_library_c_file} +) + +set(gateway_h_sources + ./inc/message.h + ./inc/message_bus.h + ./inc/module.h + ./inc/gateway_ll.h + ./inc/gateway.h + ./inc/module_loader.h + ./inc/dynamic_library.h +) + +include_directories(./inc) + +add_library(gateway + ${gateway_c_sources} + ${gateway_h_sources} +) + +target_link_libraries(gateway aziotsharedutil parson ${dynamic_loader_library}) + +#this adds the tests to the build process +add_subdirectory(tests) \ No newline at end of file diff --git a/core/README.md b/core/README.md new file mode 100644 index 00000000..e8217fe2 --- /dev/null +++ b/core/README.md @@ -0,0 +1 @@ +# Core \ No newline at end of file diff --git a/core/adapters/dynamic_library_linux.c b/core/adapters/dynamic_library_linux.c new file mode 100644 index 00000000..c90839a8 --- /dev/null +++ b/core/adapters/dynamic_library_linux.c @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "dynamic_library.h" +#include "gb_library.h" + +/* Codes_SRS_DYNAMIC_LIBRARY_17_001: [DynamicLibrary_LoadLibrary shall make the OS system call to load the named library, returning an opaque pointer as a library reference.] */ +DYNAMIC_LIBRARY_HANDLE DynamicLibrary_LoadLibrary(const char* dynamicLibraryFileName) +{ + return dlopen(dynamicLibraryFileName, RTLD_LAZY); +} + +/*Codes_SRS_DYNAMIC_LIBRARY_17_002: [DynamicLibrary_UnloadLibrary shall make the OS system call to unload the library referenced by libraryHandle.] */ +void DynamicLibrary_UnloadLibrary(DYNAMIC_LIBRARY_HANDLE libraryHandle) +{ + dlclose(libraryHandle); +} + +/*Codes_SRS_DYNAMIC_LIBRARY_17_003: [DynamicLibrary_FindSymbol shall make the OS system call to look up symbolName in the library referenced by libraryHandle.]*/ +void* DynamicLibrary_FindSymbol(DYNAMIC_LIBRARY_HANDLE libraryHandle, const char* symbolName) +{ + return dlsym(libraryHandle, symbolName); +} + diff --git a/core/adapters/dynamic_library_windows.c b/core/adapters/dynamic_library_windows.c new file mode 100644 index 00000000..63166172 --- /dev/null +++ b/core/adapters/dynamic_library_windows.c @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +#include "gb_library.h" + +#include "dynamic_library.h" +#include "azure_c_shared_utility/iot_logging.h" + +/* Codes_SRS_DYNAMIC_LIBRARY_17_001: [DynamicLibrary_LoadLibrary shall make the OS system call to load the named library, returning an opaque pointer as a library reference.] */ +DYNAMIC_LIBRARY_HANDLE DynamicLibrary_LoadLibrary(const char* dynamicLibraryFileName) +{ + DYNAMIC_LIBRARY_HANDLE returnValue = LoadLibraryA(dynamicLibraryFileName); + if (returnValue == NULL) + { + DWORD error = GetLastError(); + DWORD status; + char currentPath[MAX_PATH]; + + LogError("Error Loading Library. Error code is: %u", error); + + //This retry was needed because when you run multiple tests together LoadLibraryA failed to find the Library. + //So we need to help by building the string with current Path. + status = GetCurrentDirectoryA(MAX_PATH, currentPath); + + if (status != 0) + { + size_t totalAllocationNeeded = 0; + char* libraryPath; + totalAllocationNeeded += strlen(currentPath); + totalAllocationNeeded += strlen(dynamicLibraryFileName); + + //Allocate speace for current Path + \\ + dynamic Library FileName + NULLTerminate. + libraryPath = (char*)malloc(totalAllocationNeeded + 2); + + if (libraryPath != NULL) + { + int sprintfReturnCode; + + sprintfReturnCode = sprintf(libraryPath, "%s\\%s", currentPath, dynamicLibraryFileName); + + if (sprintfReturnCode != -1) + { + returnValue = LoadLibraryA(libraryPath); + } + else + { + LogError("CurrentPath + Library name too long and doesn't fit on MAX_PATH."); + returnValue = NULL; + } + free(libraryPath); + } + else + { + LogError("Failed to allocate memory for library path."); + returnValue = NULL; + } + + } + else + { + error = GetLastError(); + LogError("Could not retrieve Current Directory. Error: %u", error); + returnValue = NULL; + } + } + + return returnValue; +} + +/*Codes_SRS_DYNAMIC_LIBRARY_17_002: [DynamicLibrary_UnloadLibrary shall make the OS system call to unload the library referenced by libraryHandle.] */ +void DynamicLibrary_UnloadLibrary(DYNAMIC_LIBRARY_HANDLE libraryHandle) +{ + //"Module" here is not to be confused with modules at the gateway level. + HMODULE hModule = (HMODULE)libraryHandle; + FreeLibrary(hModule); +} + +/*Codes_SRS_DYNAMIC_LIBRARY_17_003: [DynamicLibrary_FindSymbol shall make the OS system call to look up symbolName in the library referenced by libraryHandle.]*/ +void* DynamicLibrary_FindSymbol(DYNAMIC_LIBRARY_HANDLE libraryHandle, const char* symbolName) +{ + //"Module" here is not to be confused with modules at the gateway level. + HMODULE hModule = (HMODULE)libraryHandle; + return (void*)GetProcAddress(hModule, symbolName); +} diff --git a/core/adapters/gb_library_linux.c b/core/adapters/gb_library_linux.c new file mode 100644 index 00000000..f4f6a9fb --- /dev/null +++ b/core/adapters/gb_library_linux.c @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +#include "gb_library.h" +void *gb_dlopen(const char *__file, int __mode) +{ + return dlopen(__file, __mode); +} + +int gb_dlclose(void *__handle) +{ + return dlclose(__handle); +} + +void *gb_dlsym(void * __handle, const char * __name) +{ + return dlsym(__handle, __name); +} diff --git a/core/adapters/gb_library_windows.c b/core/adapters/gb_library_windows.c new file mode 100644 index 00000000..256bdca1 --- /dev/null +++ b/core/adapters/gb_library_windows.c @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +#include "gb_library.h" + +void* gb_LoadLibraryA(const char* dynamicLibraryFileName) +{ + return (void*)LoadLibraryA(dynamicLibraryFileName); +} + + +int gb_FreeLibrary(void* library) +{ + return (int)FreeLibrary((HMODULE)library); +} + + +void* gb_GetProcAddress(void* library, const char* symbolName) +{ + return (void*)GetProcAddress((HMODULE)library, symbolName); +} + +DWORD gb_GetLastError() +{ + return GetLastError(); +} + +DWORD gb_GetCurrentDirectoryA(DWORD nBufferLength, char* lpBuffer) +{ + return GetCurrentDirectoryA(nBufferLength, lpBuffer); +} + diff --git a/core/deps/CMakeLists.txt b/core/deps/CMakeLists.txt new file mode 100644 index 00000000..e5c85d18 --- /dev/null +++ b/core/deps/CMakeLists.txt @@ -0,0 +1,21 @@ +#this is CMakeLists.txt to build parson only as a lib +cmake_minimum_required(VERSION 2.8.11) + +remove_definitions(-D_CRT_SECURE_NO_WARNINGS) + +set(parson_c_sources + ../../deps/parson/parson.c +) + +set(parson_h_sources + ../../deps/parson/parson.h +) + +include_directories(../../deps/parson) + +remove_definitions(-D_CRT_SECURE_NO_WARNINGS) + +add_library(parson + ${parson_c_sources} + ${parson_h_sources} +) \ No newline at end of file diff --git a/core/devdoc/2015 01 13 FGW architecture.pptx b/core/devdoc/2015 01 13 FGW architecture.pptx new file mode 100644 index 00000000..4c42a4be Binary files /dev/null and b/core/devdoc/2015 01 13 FGW architecture.pptx differ diff --git a/core/devdoc/dynamic_library_requirements.md b/core/devdoc/dynamic_library_requirements.md new file mode 100644 index 00000000..41e36526 --- /dev/null +++ b/core/devdoc/dynamic_library_requirements.md @@ -0,0 +1,47 @@ +# dynamic_library Requirements + + +  +## Overview +dynamic_library is a wrapper for OS system calls for loading, unloading and +finding a symbol in a dynamically linked library. + +## References +none + +## Exposed API +```C +typedef void* DYNAMIC_LIBRARY_HANDLE; + +extern DYNAMIC_LIBRARY_HANDLE DynamicLibrary_LoadLibrary(const char* dynamicLibraryFileName); +extern void DynamicLibrary_UnloadLibrary(DYNAMIC_LIBRARY_HANDLE libraryHandle); +extern void* DynamicLibrary_FindSymbol(DYNAMIC_LIBRARY_HANDLE libraryHandle, const char* symbolName); +``` + +### DynamicLibrary_LoadLibrary +```C +extern DYNAMIC_LIBRARY_HANDLE DynamicLibrary_LoadLibrary(const char* dynamicLibraryFileName); +``` + +**SRS_DYNAMIC_LIBRARY_17_001: [**`DynamicLibrary_LoadLibrary` shall make the OS system call to load the named library, returning an opaque pointer as a library reference.**]** + +In Linux, this will be "dlopen" and in Windows, this will be "LoadLibrary." + +### DynamicLibrary_UnloadLibrary +```C +extern void DynamicLibrary_UnloadLibrary(DYNAMIC_LIBRARY_HANDLE libraryHandle); +``` + +**SRS_DYNAMIC_LIBRARY_17_002: [**`DynamicLibrary_UnloadLibrary` shall make the OS system call to unload the library referenced by libraryHandle.**]** + +In Linux, this will be "dlclose" and in Windows, this will be "FreeLibrary." + +### DynamicLibrary_FindSymbol +```C +extern void* DynamicLibrary_FindSymbol(DYNAMIC_LIBRARY_HANDLE libraryHandle, const char* symbolName); +``` + +**SRS_DYNAMIC_LIBRARY_17_003: [**`DynamicLibrary_FindSymbol` shall make the OS system call to look up symbolName in the library referenced by libraryHandle.**]** + +In Linux, this will be "dlsym" and in Windows, this will be "GetProcAddress." + \ No newline at end of file diff --git a/core/devdoc/gateway_ll_requirements.md b/core/devdoc/gateway_ll_requirements.md new file mode 100644 index 00000000..2b3dcb0e --- /dev/null +++ b/core/devdoc/gateway_ll_requirements.md @@ -0,0 +1,168 @@ +# Gateway_LL Requirements + +## Overview +This is the API to create and manage a gateway. Contained within a gateway is a message bus onto which any number of modules may be loaded. This gateway API is essentially a convenience layer facilitating the easy addition and removal of modules from the message bus. + +## References + +##Gateway Handle Implementation + +This section details the internal structure defined in the gateway implementation used to track a gateway and the information it contains. + +``` +typedef struct GATEWAY_HANDLE_DATA_TAG { + /** @brief Vector of MODULE_DATA modules that the Gateway must track */ + VECTOR_HANDLE modules; + + /** @brief The message bus contained within this Gateway */ + MESSAGE_BUS_HANDLE bus; +} GATEWAY_HANDLE; +``` + +## Exposed API +``` +#ifndef GATEWAY_LL_H +#define GATEWAY_LL_H + +#include "macro_utils.h" +#include "module.h" +#include "azure_c_shared_utility/vector.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct GATEWAY_HANDLE_DATA_TAG* GATEWAY_HANDLE; + +/** @brief This struct represents a single entry of the GATEWAY_PROPERTIES. */ +typedef struct GATEWAY_PROPERTIES_ENTRY_TAG +{ + /** @brief The (possibly NULL) name of the module */ + const char* module_name; + + /** @brief The path to the .dll or .so of the module */ + const char* module_path; + + /** @brief The user-defined properties object for the module */ + const void* module_properties; +} GATEWAY_PROPERTIES_ENTRY; + +/** @brief This struct represents the properties that should be used when creating a module. */ +typedef struct GATEWAY_PROPERTIES_DATA_TAG +{ + /** @brief Vector of GATEWAY_PROPERTIES_ENTRY objects. */ + VECTOR_HANDLE gateway_properties_entries; +} GATEWAY_PROPERTIES; + +/** @breif Creates a new gateway using the provided GATEWAY_PROPERTIES and returns a GATEWAY_HANDLE for the newly created gateway */ +extern GATEWAY_HANDLE Gateway_LL_Create(const GATEWAY_PROPERTIES* properties); + +/** @brief Destroys gw and all associated data. */ +extern void Gateway_LL_Destroy(GATEWAY_HANDLE gw); + +/** @brief Creates a new module based on the GATEWAY_PROPERTIES_ENTRY* and returns a MODULE_HANDLE if successful, NULL otherwise. */ +extern MODULE_HANDLE Gateway_LL_AddModule(GATEWAY_HANDLE gw, const GATEWAY_PROPERTIES_ENTRY* entry); + +/** @brief Removes the provided module from the gateway */ +extern void Gateway_LL_RemoveModule(GATEWAY_HANDLE gw, MODULE_HANDLE module); + +#ifdef __cplusplus +} +#endif + +#endif // GATEWAY_LL_H + +``` + +##Gateway_Create +``` +extern GATEWAY_HANDLE Gateway_LL_Create(const GATEWAY_PROPERTIES* properties); +``` +Gateway_LL_Create creates a new gateway using information about modules in the `GATEWAY_PROPERTIES` struct to initialize modules on the message bus. + +**SRS_GATEWAY_LL_14_001: [** This function shall create a `GATEWAY_HANDLE` representing the newly created gateway. **]** + +**SRS_GATEWAY_LL_14_002: [** This function shall return `NULL` upon any memory allocation failure. **]** + +**SRS_GATEWAY_LL_14_003: [** This function shall create a new `MESSAGE_BUS_HANDLE` for the gateway representing this gateway's message bus. **]** + +**SRS_GATEWAY_LL_14_004: [** This function shall return `NULL` if a `MESSAGE_BUS_HANDLE` cannot be created. **]** + +**SRS_GATEWAY_LL_14_033: [** The function shall create a vector to store each `MODULE_DATA`. **]** + +**SRS_GATEWAY_LL_14_034: [** This function shall return `NULL` if a `VECTOR_HANDLE` cannot be created. **]** + +**SRS_GATEWAY_LL_14_035: [** This function shall destroy the previously created `MESSAGE_BUS_HANDLE` and free the `GATEWAY_HANDLE` if the `VECTOR_HANDLE` cannot be created. **]** + +**SRS_GATEWAY_LL_14_009: [** The function shall use each `GATEWAY_PROPERTIES_ENTRY` use each of `GATEWAY_PROPERTIES`'s `gateway_properties_entries` to create and add a `MODULE_HANDLE` to the `GATEWAY_HANDLE` message bus. **]** + +**SRS_GATEWAY_LL_14_036: [** If any `MODULE_HANDLE` is unable to be created from a `GATEWAY_PROPERTIES_ENTRY` the `GATEWAY_HANDLE` will be destroyed. **]** + +##Gateway_Destroy +``` +extern void Gateway_LL_Destroy(GATEWAY_HANDLE gw); +``` +Gateway_LL_Destroy destroys a gateway represented by the `gw` parameter. + +**SRS_GATEWAY_LL_14_005: [** If `gw` is `NULL` the function shall do nothing. **]** + +**SRS_GATEWAY_LL_14_028: [** The function shall remove each module in `GATEWAY_HANDLE_DATA`'s `modules` vector and destroy `GATEWAY_HANDLE_DATA`'s `modules`. **]** + +**SRS_GATEWAY_LL_14_037: [** If `GATEWAY_HANDLE_DATA`'s message bus cannot unlink module, the function shall log the error and continue unloading the module from the `GATEWAY_HANDLE`. **]** + +**SRS_GATEWAY_LL_14_006: [** The function shall destroy the `GATEWAY_HANDLE_DATA`'s `bus` `MESSAGE_BUS_HANDLE`. **]** + +##Gateway_AddModule +``` +extern MODULE_HANDLE Gateway_LL_AddModule(GATEWAY_HANDLE gw, const GATEWAY_PROPERTIES_ENTRY* entry); +``` +Gateway_LL_AddModule adds a module to the gateway message bus using the provided `GATEWAY_PROPERTIES_ENTRY`'s `module_path` and `GATEWAY_PROPERTIES_ENTRY`'s `module_properties`. + +**SRS_GATEWAY_LL_14_011: [** If `gw`, `entry`, or `GATEWAY_PROPERTIES_ENTRY`'s `module_path` is `NULL` the function shall return `NULL`. **]** + +**SRS_GATEWAY_LL_14_012: [** The function shall load the module located at `GATEWAY_PROPERTIES_ENTRY`'s `module_path` into a `MODULE_LIBRARY_HANDLE`. **]** + +**SRS_GATEWAY_LL_14_031: [** If unsuccessful, the function shall return `NULL`. **]** + +**SRS_GATEWAY_LL_14_013: [** The function shall get the `const MODULE_APIS*` from the `MODULE_LIBRARY_HANDLE`. **]** + +**SRS_GATEWAY_LL_14_015: [** The function shall use the `MODULE_APIS` to create a `MODULE_HANDLE` using the `GATEWAY_PROPERTIES_ENTRY`'s `module_properties`. **]** + +**SRS_GATEWAY_LL_14_016: [** If the module creation is unsuccessful, the function shall return `NULL`. **]** + +**SRS_GATEWAY_LL_14_017: [** The function shall link the module to the `GATEWAY_HANDLE_DATA`'s `bus` using a call to `MessageBus_AddModule`. **]** + +**SRS_GATEWAY_LL_14_039: [** The function shall increment the `MESSAGE_BUS_HANDLE` reference count if the `MODULE_HANDLE` was successfully linked to the `GATEWAY_HANDLE_DATA`'s `bus`. **]** + +**SRS_GATEWAY_LL_14_018: [** If the message bus linking is unsuccessful, the function shall return `NULL`. **]** + +**SRS_GATEWAY_LL_14_029: [** The function shall create a new `MODULE_DATA` containting the `MODULE_HANDLE` and `MODULE_LIBRARY_HANDLE` if the module was successfully linked to the message bus. **]** + +**SRS_GATEWAY_LL_14_032: [** The function shall add the new `MODULE_DATA` to `GATEWAY_HANDLE_DATA`'s `modules` if the module was successfully linked to the message bus. **]** + +**SRS_GATEWAY_LL_14_030: [** If any internal API call is unsuccessful after a module is created, the library will be unloaded and the module destroyed. **]** + +**SRS_GATEWAY_LL_14_019: [** The function shall return the newly created `MODULE_HANDLE` only if each API call returns successfully. **]** + +##Gateway_RemoveModule +``` +extern void Gateway_LL_RemoveModule(GATEWAY_HANDLE gw, MODULE_HANDLE module); +``` +Gateway_RemoveModule will remove the specified `module` from the message bus. + +**SRS_GATEWAY_LL_14_020: [** If `gw` or `module` is `NULL` the function shall return. **]** + +**SRS_GATEWAY_LL_14_023: [** The function shall locate the `MODULE_DATA` object in `GATEWAY_HANDLE_DATA`'s `modules` containing `module` and return if it cannot be found. **]** + +**SRS_GATEWAY_LL_14_021: [** The function shall unlink `module` from the `GATEWAY_HANDLE_DATA`'s `bus` `MESSAGE_BUS_HANDLE`. **]** + +**SRS_GATEWAY_LL_14_022: [** If `GATEWAY_HANDLE_DATA`'s `bus` cannot unlink `module`, the function shall log the error and continue unloading the module from the `GATEWAY_HANDLE`. **]** + +**SRS_GATEWAY_LL_14_038: [** The function shall decrement the `MESSAGE_BUS_HANDLE` reference count. **]** + +**SRS_GATEWAY_LL_14_024: [** The function shall use the `MODULE_DATA`'s `library_handle` to retrieve the `MODULE_APIS` and destroy `module`. **]** + +**SRS_GATEWAY_LL_14_025: [** The function shall unload `MODULE_DATA`'s `library_handle`. **]** + +**SRS_GATEWAY_LL_14_026: [** The function shall remove that `MODULE_DATA` from `GATEWAY_HANDLE_DATA`'s `modules`. **]** \ No newline at end of file diff --git a/core/devdoc/gateway_requirements.md b/core/devdoc/gateway_requirements.md new file mode 100644 index 00000000..bfb50881 --- /dev/null +++ b/core/devdoc/gateway_requirements.md @@ -0,0 +1,74 @@ +# Gateway Requirements + +## Overview +This is the higher level API to create and manage a gateway. Contained within a gateway is a message bus onto which any number of modules may be loaded. This gateway API is essentially a convenience layer facilitating the easy addition and removal of modules from the message bus. + +## References + +### Parson +Third party C JSON library: https://github.com/kgabis/parson + +## JSON Structure + +```json +{ + "modules" : + [ + { + "module name" : "foo", + "module path" : "F:\\foo.dll", + "args" : ... + }, + { + "module name" : "bar", + "module path" : "F:\\bar.dll", + "args" : ... + }, + ... + ] +} +``` + +## Exposed API +``` +#ifndef GATEWAY_H +#define GATEWAY_H + +#include "gateway_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern GATEWAY_HANDLE Gateway_Create_From_JSON(const char* file_path); + +#ifdef __cplusplus +} +#endif + +#endif // GATEWAY_H + +``` + +##Gateway_Create_From_JSON +``` +extern GATEWAY_HANDLE Gateway_Create_From_JSON(const char* file_path); +``` +Gateway_Create_From_JSON creates a new gateway using information contained within a well-formed JSON configuration file. The JSON string should be formatted as described above. + +**SRS_GATEWAY_14_001: [** If `file_path` is NULL the function shall return NULL. **]** + +**SRS_GATEWAY_14_002: [** The function shall use *parson* to read the file and parse the JSON string to a *parson* `JSON_Value` structure. **]** + +**SRS_GATEWAY_14_003: [** The function shall return NULL if the file contents could not be read and/or parsed to a `JSON_Value`. **]** + +**SRS_GATEWAY_14_004: [** The function shall traverse the `JSON_Value` object to initialize a `GATEWAY_PROPERTIES` instance. **]** + +**SRS_GATEWAY_14_005: [** The function shall set the value of `const void* module_properties` in the `GATEWAY_PROPERTIES` instance to a char\* representing the serialized *args* value for the particular module. **]** + +**SRS_GATEWAY_14_006: [** The function shall return NULL if the `JSON_Value` contains incomplete information. **]** + +**SRS_GATEWAY_14_007: [** The function shall use the `GATEWAY_PROPERTIES` instance to create and return a `GATEWAY_HANDLE` using the lower level API. **]** + +**SRS_GATEWAY_14_008: [** This function shall return `NULL` upon any memory allocation failure. **]** \ No newline at end of file diff --git a/core/devdoc/message_bus_hld.md b/core/devdoc/message_bus_hld.md new file mode 100644 index 00000000..37e80b5e --- /dev/null +++ b/core/devdoc/message_bus_hld.md @@ -0,0 +1,130 @@ +Message Bus +=========== + +High level design +----------------- + +### Overview + +The message bus (or just the "bus") is central to the gateway. The message bus plays the role of a message broker - a central agent responsible for receiving and broadcasting messages between interested parties. In case of the gateway, the interested parties will be *modules*. This document describes the high level design of the message bus along with descriptions of flow of control. + +### Design Goals + +The message bus essentially has two underlying guiding principles that influence its design: + +- The message bus APIs shall be thread-safe, i.e., any piece of code running on any arbitrary thread is allowed to call any API on the message bus at any time concurrently or otherwise. +- When the bus delivers messages to modules on the bus it shall ensure that it does so in a serial fashion for any given module, i.e., at no point shall it *concurrently* invoke the message receive function on a module. This allows modules to not have to concern themselves with ensuring synchronization of access to shared state on the module. + +### The Message Bus Data + +An instance of the message bus is represented in code via an opaque `MESSAGE_BUS_HANDLE` value and is a reference counted object. The `MESSAGE_BUS_HANDLE` is backed by a data structure called `MESSAGE_BUS_HANDLE_DATA` which looks like this: + +```C +typedef struct MESSAGE_BUS_HANDLE_DATA_TAG +{ + VECTOR_HANDLE modules; + LOCK_HANDLE modules_lock; +}MESSAGE_BUS_HANDLE_DATA; +``` + +Here's a description of the members of `MESSAGE_BUS_HANDLE_DATA`. + +>| Field | Description | +>|-------------|------------------------------------------------------------------------------| +>| modules | Vector of modules where each element is an instance of `MODULE_INFO`. | +>| modules_lock | A mutex used to synchronize access to the `modules` field. | + +### The Module Info + +Each module that is connected to the message bus is represented using a structure of type `MODULE_INFO` which looks like so: + +```C +typedef struct MODULE_INFO_TAG +{ + MODULE_HANDLE module; + MODULE_APIS module_apis; + THREAD_HANDLE thread; + VECTOR_HANDLE mq; + COND_HANDLE mq_cond; + LOCK_HANDLE mq_lock; + sig_atomic_t quit_worker; +}MODULE_INFO; +``` + +A description of the fields in the `MODULE_INFO` structure follows: + +>| Field | Description | +>|-------------|----------------------------------------------------------------------| +>| module | Reference to the module. | +>| module_apis | The function dispatch table for this module. | +>| thread | Handle to the thread on which this module's message loop is running. | +>| mq | A queue of messages that are due for delivery to this module. | +>| mq_cond | A condition variable that is signaled when there are new messages. | +>| mq_lock | A mutex used to synchronize access to the `mq` field. | +>| quit_worker | Message publish worker will keep running while this is `0`. | + +### Adding A Module To The Message Bus + +Whenever a new module is added to the message bus a new thread is created and launched whose responsibility it is to process messages that are delivered for that module by invoking the module's message callback function. The worker thread will wait on the condition variable `mq_cond` and continuously deliver messages to the module whenever `mq_cond` is signalled. If `quit_worker` is equal to `1` then the worker thread will quit and return. + +### Publishing A Message + +Publishing a message to the bus essentially involves two discrete phases: + +1. Queue the message to each module's message queue (the `mq` field in the module's `MODULE_INFO` structure). +2. Signal the `mq_cond` condition variable to cause the worker thread associated with the module to wake up and process the message in the queue. + +Here are the sequence of steps the publish function performs: + +**Code Segment 1** +```C +01: MESSAGE_BUS_HANDLE_DATA message_bus_data = message_bus_handle +02: Lock message_bus_data.modules_lock +``` + +> **NOTE**: Acquiring a lock on the entire `modules` vector is a tad too coarse grained; we could consider enhancing this in the future to use a linked list and use more fine grained locking strategies - for e.g. a sliding window lock where we lock access only to the current, previous and next modules for each module in the list. +> +> Another alternative might be to consider using lock-free collections using atomic operations. This must however be adopted only if we have: +> +> - a performance problem due to excessive lock contention, and +> - a way of measuring performance post switching to a lock-free alternative so that we have a reliable mechanism for ascertaining that the alternative made things better. + +```C +03: for each module in message_bus_data.modules +04: { +05: Lock module.mq_lock +06: MESSAGE_HANDLE msg = Clone input_msg (increment ref count) +07: Append msg to module.mq +08: Unlock module.mq_lock +09: Signal module.mq_cond +10: } +11: Unlock message_bus_data.modules_lock +``` + +### Module Publish Worker + +The `module_publish_worker` function is passed in a pointer to the relevant `MODULE_INFO` object as it's thread context parameter. The function's job is to basically wait on the `mq_cond` condition variable and process messages in `module.mq` when the condition is signalled. Here's the pseudo-code implementation of what it does: + +**Code Segment 2** +```C +01: MODULE_INFO module_info = context +02: while(module_info.quit_worker == 0) +03: { +04: Lock module_info.mq_lock +05: Wait on module_info.mq_cond, module.mq_lock +06: while( +07: module_info.quit_worker == 0 && +08: module_info.mq is not empty +09: ) +10: { +11: MESSAGE_HANDLE msg = Dequeue module_info.mq +12: Unlock module_info.mq_lock +13: Deliver msg to module_info.module +14: Destroy msg (decrement ref count) +15: Lock module_info.mq_lock +16: } +17: } +18: Unlock module_info.mq_lock +``` + +In other words, this function keeps delivering messages to the module while the module's message queue is not empty. Note that new messages can potentially be concurrently en-queued to the module's message queue while line **14** is being executed. diff --git a/core/devdoc/message_bus_requirements.md b/core/devdoc/message_bus_requirements.md new file mode 100644 index 00000000..8dacdc33 --- /dev/null +++ b/core/devdoc/message_bus_requirements.md @@ -0,0 +1,266 @@ +# Message Bus + +## Overview + +The message bus (or just the "bus") is central to the gateway. The bus plays the role of a message broker - a central agent responsible for receiving and broadcasting messages between interested parties. In case of the gateway, the interested parties will be modules. This document describes the API for the message bus and what some of the threading implications are. + +## References + +* [Message Bus High Level Design](bus_hld.md) +* `module.h` - [Module API requirements](module.md) +* [Message API requirements](message_requirements.md) + +## Tracking Modules + +The message bus implementation shall use the following structure definition to track each module that's connected to the bus: + +```C +typedef struct MESSAGE_BUS_MODULEINFO_TAG +{ + /** + * Handle to the module that's connected to the message bus. + */ + MODULE_HANDLE module; + + /** + * The function dispatch table for this module. + */ + CONST MODULE_APIS* module_apis; + + /** + * Handle to the thread on which this module’s message processing loop is + * running. + */ + THREAD_HANDLE thread; + + /** + * Handle to the queue of messages to be delivered to this module. + */ + VECTOR_HANDLE mq; + + /** + * Lock used to synchronize access to the 'mq' field. + */ + LOCK_HANDLE mq_lock; + + /** + * A condition variable that is signaled when there are new messages. + */ + COND_HANDLE mq_cond; + + /** + * Message publish worker will keep running while this is false. + */ + sig_atomic_t quit_worker; +}MESSAGE_BUS_MODULEINFO; +``` + +## Message Bus API + +```C +typedef struct MESSAGE_BUS_HANDLE_DATA_TAG* MESSAGE_BUS_HANDLE; + +#define MESSAGE_BUS_RESULT_VALUES \ + MESSAGE_BUS_OK, \ + MESSAGE_BUS_ERROR, \ + MESSAGE_BUS_INVALIDARG + +DEFINE_ENUM(MESSAGE_BUS_RESULT, MESSAGE_BUS_RESULT_VALUES); + +extern MESSAGE_BUS_HANDLE MESSAGE_extern MESSAGE_BUS_HANDLE MessageBus_Create(void); +extern void MessageBus_IncRef(MESSAGE_BUS_HANDLE bus); +extern void MessageBus_DecRef(BUS_HANDLE bus); +extern MESSAGE_BUS_RESULT MessageBus_Publish(MESSAGE_BUS_HANDLE bus, MESSAGE_HANDLE message); +extern MESSAGE_BUS_RESULT MessageBus_AddModule(MESSAGE_BUS_HANDLE bus, MODULE_HANDLE module, const MODULE_APIS* module_apis); +extern MESSAGE_BUS_RESULT MessageBus_RemoveModule(MESSAGE_BUS_HANDLE bus, MODULE_HANDLE module); +extern void MessageBus_Destroy(MESSAGE_BUS_HANDLE bus); +``` + +## MessageBus_Create +```C +MESSAGE_BUS_HANDLE MessageBus_Create(void) +``` + +**SRS_MESSAGE_BUS_13_001: [** This API shall yield a `MESSAGE_BUS_HANDLE` representing the newly created message bus. This handle value shall not be equal to `NULL` when the API call is successful. **]** + +**SRS_MESSAGE_BUS_13_003: [** This function shall return `NULL` if an underlying API call to the platform causes an error. **]** + +The message bus implementation shall use the following definition as the backing structure for the message bus handle: + +```C +typedef struct MESSAGE_BUS_HANDLE_DATA_TAG +{ + /** + * List of modules that are attached to this message bus. Each element in this + * vector is an instance of MESSAGE_BUS_MODULEINFO. + */ + VECTOR_HANDLE modules; + + /** + * Lock used to synchronize access to the 'modules' field. + */ + LOCK_HANDLE modules_lock; +}MESSAGE_BUS_HANDLE_DATA; +``` + +**SRS_MESSAGE_BUS_13_067: [** `MessageBus_Create` shall `malloc` a new instance of `BUS_HANDLE_DATA` and return `NULL` if it fails. **]** + +**SRS_MESSAGE_BUS_13_007: [** `MessageBus_Create` shall initialize `BUS_HANDLE_DATA::modules` with a valid `VECTOR_HANDLE`. **]** + +**SRS_MESSAGE_BUS_13_023: [** `MessageBus_Create` shall initialize `BUS_HANDLE_DATA::modules_lock` with a valid `LOCK_HANDLE`. **]** + +## MessageBus_IncRef + +```C +void MessageBus_IncRef(MESSAGE_BUS_HANDLE bus); +``` +MessageBus_Clone creates a clone of the message bus handle. + +**SRS_MESSAGE_BUS_13_108: [** If `bus` is `NULL` then `MessageBus_IncRef` shall do nothing. **]** + +**SRS_MESSAGE_BUS_13_109: [** Otherwise, `MessageBus_IncRef` shall increment the internal ref count. **]** + +## module_publish_worker + +```C +static void module_publish_worker(void* user_data) +``` + +**SRS_MESSAGE_BUS_13_026: [** This function shall assign `user_data` to a local variable called `module_info` of type `MESSAGE_BUS_MODULEINFO*`. **]** + +**SRS_MESSAGE_BUS_13_089: [** This function shall acquire the lock on `module_info->mq_lock`. **]** + +**SRS_MESSAGE_BUS_02_004: [** If acquiring the lock fails, then module_publish_worker shall return. **]** + +**SRS_MESSAGE_BUS_13_068: [** This function shall run a loop that keeps running while `module_info->quit_worker` is equal to `0`. **]** + +**SRS_MESSAGE_BUS_04_001: [** This function shall immediately start processing messages when `module->mq` is not empty without waiting on `module->mq_cond`. **]** + +**SRS_MESSAGE_BUS_13_071: [** For every iteration of the loop the function will first wait on `module_info->mq_cond` using `module_info->mq_lock` as the corresponding mutex to be used by the condition variable. **]** + +**SRS_MESSAGE_BUS_13_090: [** When `module_info->mq_cond` has been signaled this function shall kick off another loop predicated on `module_info->quit_worker` being equal to `0` and `module_info->mq` not being empty. This thread has the lock on `module_info->mq_lock` at this point. **]** + +**SRS_MESSAGE_BUS_13_069: [** The function shall dequeue a message from the module's message queue. **]** + +**SRS_MESSAGE_BUS_13_091: [** The function shall unlock `module_info->mq_lock`. **]** + +**SRS_MESSAGE_BUS_13_092: [** The function shall deliver the message to the module's callback function via `module_info->module_apis`. **]** + +**SRS_MESSAGE_BUS_13_093: [** The function shall destroy the message that was dequeued by calling `Message_Destroy`. **]** + +**SRS_MESSAGE_BUS_13_094: [** The function shall re-acquire the lock on `module_info->mq_lock`. **]** + +**SRS_MESSAGE_BUS_13_095: [** When the function exits the outer loop predicated on `module_info->quit_worker` being `0` it shall unlock `module_info->mq_lock` before exiting from the function. **]** + +## MessageBus_Publish + +```C +MESSAGE_BUS_RESULT MessageBus_Publish( + MESSAGE_BUS_HANDLE bus, + MESSAGE_HANDLE message +); +``` + +**SRS_MESSAGE_BUS_13_030: [** If `bus` or `message` is `NULL` the function shall return `MESSAGE_BUS_INVALIDARG`. **]** + +**SRS_MESSAGE_BUS_13_031: [** `MessageBus_Publish` shall acquire the lock `MESSAGE_BUS_HANDLE_DATA::modules_lock`. **]** + +**SRS_MESSAGE_BUS_13_032: [** `MessageBus_Publish` shall start a processing loop for every module in `MESSAGE_BUS_HANDLE_DATA::modules`. **]** + +**SRS_MESSAGE_BUS_13_033: [** In the loop, the function shall first acquire the lock on `MESSAGE_BUS_MODULEINFO::mq_lock`. **]** + +**SRS_MESSAGE_BUS_13_034: [** The function shall then append `message` to `MESSAGE_BUS_MODULEINFO::mq` by calling `Message_Clone` and `VECTOR_push_back`. **]** + +**SRS_MESSAGE_BUS_13_035: [** The function shall then release `MESSAGE_BUS_MODULEINFO::mq_lock`. **]** + +**SRS_MESSAGE_BUS_13_096: [** The function shall then signal `MESSAGE_BUS_MODULEINFO::mq_cond`. **]** + +**SRS_MESSAGE_BUS_13_040: [** `MessageBus_Publish` shall release the lock `MESSAGE_BUS_HANDLE_DATA::modules_lock` after the loop. **]** + +**SRS_MESSAGE_BUS_13_037: [** This function shall return `MESSAGE_BUS_ERROR` if an underlying API call to the platform causes an error or `MESSAGE_BUS_OK` otherwise. **]** + +## MessageBus_AddModule + +```C +MESSAGE_BUS_RESULT MessageBus_AddModule(MESSAGE_BUS_HANDLE bus, MODULE_HANDLE module, const MODULE_APIS* module_apis) +``` + +**SRS_MESSAGE_BUS_13_038: [** If `bus` or `module` or `module_apis` is `NULL` the function shall return `MESSAGE_BUS_INVALIDARG`. **]** + +**SRS_MESSAGE_BUS_13_097: [** The function shall assign `module_apis` to `MESSAGE_BUS_MODULEINFO::module_apis`. **]** + +**SRS_MESSAGE_BUS_13_107: [** The function shall assign the `module` handle to `MESSAGE_BUS_MODULEINFO::module`. **]** + +**SRS_MESSAGE_BUS_13_098: [** The function shall initialize `MESSAGE_BUS_MODULEINFO::mq` with a valid vector handle. **]** + +**SRS_MESSAGE_BUS_13_099: [** The function shall initialize `MESSAGE_BUS_MODULEINFO::mq_lock` with a valid lock handle. **]** + +**SRS_MESSAGE_BUS_13_100: [** The function shall initialize `MESSAGE_BUS_MODULEINFO::mq_cond` with a valid condition handle. **]** + +**SRS_MESSAGE_BUS_13_101: [** The function shall assign `0` to `MESSAGE_BUS_MODULEINFO::quit_worker`. **]** + +**SRS_MESSAGE_BUS_13_102: [** The function shall create a new thread for the module by calling `ThreadAPI_Create` using `module_publish_worker` as the thread callback and using the newly allocated `MESSAGE_BUS_MODULEINFO` object as the thread context. **]** + +**SRS_MESSAGE_BUS_13_039: [** This function shall acquire the lock on `MESSAGE_BUS_HANDLE_DATA::modules_lock`. **]** + +**SRS_MESSAGE_BUS_13_045: [** `MessageBus_AddModule` shall append the new instance of `MESSAGE_BUS_MODULEINFO` to `MESSAGE_BUS_HANDLE_DATA::modules`. **]** + +**SRS_MESSAGE_BUS_13_046: [** This function shall release the lock on `MESSAGE_BUS_HANDLE_DATA::modules_lock`. **]** + +**SRS_MESSAGE_BUS_13_047: [** This function shall return `MESSAGE_BUS_ERROR` if an underlying API call to the platform causes an error or `MESSAGE_BUS_OK` otherwise. **]** + +## MessageBus_RemoveModule + +```C +MESSAGE_BUS_RESULT MessageBus_RemoveModule(MESSAGE_BUS_HANDLE bus, MODULE_HANDLE module) +``` + +**SRS_MESSAGE_BUS_13_048: [** If `bus` or `module` is `NULL` the function shall return `MESSAGE_BUS_INVALIDARG`. **]** + +**SRS_MESSAGE_BUS_13_088: [** This function shall acquire the lock on `MESSAGE_BUS_HANDLE_DATA::modules_lock`. **]** + +**SRS_MESSAGE_BUS_13_049: [** `MessageBus_RemoveModule` shall perform a linear search for `module` in `MESSAGE_BUS_HANDLE_DATA::modules`. **]** + +**SRS_MESSAGE_BUS_13_050: [** `MessageBus_RemoveModule` shall unlock `MESSAGE_BUS_HANDLE_DATA::modules_lock` and return `MESSAGE_BUS_ERROR` if the module is not found in `MESSAGE_BUS_HANDLE_DATA::modules`. **]** + +**SRS_MESSAGE_BUS_13_052: [** The function shall remove the module from `MESSAGE_BUS_HANDLE_DATA::modules`. **]** + +**SRS_MESSAGE_BUS_13_054: [** This function shall release the lock on `MESSAGE_BUS_HANDLE_DATA::modules_lock`. **]** + +**SRS_MESSAGE_BUS_02_001: [** MessageBus_RemoveModule shall lock `MESSAGE_BUS_MODULEINFO::mq_lock`. **]** + +**SRS_MESSAGE_BUS_02_002: [** If locking fails, then terminating the thread shall not be attempted (signalling the condition and joining the thread). **]** + +**SRS_MESSAGE_BUS_13_103: [** The function shall assign `1` to `MESSAGE_BUS_MODULEINFO::quit_worker`. **]** + +**SRS_MESSAGE_BUS_17_001: [**The function shall signal `MESSAGE_BUS_MODULEINFO::mq_cond` to release module from waiting.**]** + +**SRS_MESSAGE_BUS_02_003: [** After signaling the condition, MessageBus_RemoveModule shall unlock `MESSAGE_BUS_MODULEINFO::mq_lock`. **]** + +**SRS_MESSAGE_BUS_13_104: [** The function shall wait for the module's thread to exit by joining `MESSAGE_BUS_MODULEINFO::thread` via `ThreadAPI_Join`. **]** + +**SRS_MESSAGE_BUS_13_056: [** If `MESSAGE_BUS_MODULEINFO::mq` is not empty then this function shall call `Message_Destroy` on every message still left in the collection. **]** + +**SRS_MESSAGE_BUS_13_057: [** The function shall free all members of the `MESSAGE_BUS_MODULEINFO` object. **]** + +**SRS_MESSAGE_BUS_13_053: [** This function shall return `MESSAGE_BUS_ERROR` if an underlying API call to the platform causes an error or `MESSAGE_BUS_OK` otherwise. **]** + +## MessageBus_Destroy + +```C +void MessageBus_Destroy(MESSAGE_BUS_HANDLE bus) +``` + +**SRS_MESSAGE_BUS_13_058: [** If `bus` is `NULL` the function shall do nothing. **]** + +**SRS_MESSAGE_BUS_13_111: [** Otherwise, MessageBus_Destroy shall decrement the internal ref count of the message. **]** + +**SRS_MESSAGE_BUS_13_112: [** If the ref count is zero then the allocated resources are freed. **]** + +## MesageBus_DecRef + +```C +void MessageBus_DecRef(MESSAGE_BUS_HANDLE bus) +``` + +**SRS_MESSAGE_BUS_13_113: [** This function shall implement all the requirements of the `MessageBus_Destroy` API. **]** diff --git a/core/devdoc/message_requirements.md b/core/devdoc/message_requirements.md new file mode 100644 index 00000000..9a421f25 --- /dev/null +++ b/core/devdoc/message_requirements.md @@ -0,0 +1,168 @@ +# message Requirements + + +  + +##Overview +This is the API to create a gateway message. +Messages, once created, should be considered immutable by the consuming user code. +Modules create messages. Modules publish the said messages to the message bus. +The message bus feeds the messages to the consumers. +Messages on the message bus have a bag of properties (name, value) and an opaque array of bytes that is the message content. + +The creation of the message is considered finished at the moment when the message is transferred from the producer to the consumer. + +##References + +[constmap.h](../azure-c-shared-utility/c/devdoc/constmap_requirements.md) + +[constbuffer.h](../azure-c-shared-utility/c/devdoc/constbuffer_requirements.md) + +##Exposed API +```C +#ifndef MESSAGE_H +#define MESSAGE_H + +#ifdef __cplusplus +#include +#include +extern "C" +{ +#else +#include +#include +#endif + +#include "macro_utils.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/constmap.h" +#include "azure_c_shared_utility/constbuffer.h" +#include "azure_c_shared_utility/buffer_.h" + +/*this is the interface of any message*/ + +typedef struct MESSAGE_HANDLE_DATA_TAG* MESSAGE_HANDLE; + +/*all messages are constructed from this */ +typedef struct MESSAGE_CONFIG_TAG +{ + size_t size; + const unsigned char* source; + MAP_HANDLE sourceProperties; +}MESSAGE_CONFIG; + + +typedef struct MESSAGE_BUFFER_CONFIG_TAG +{ + CONSTBUFFER_HANDLE sourceContent; + MAP_HANDLE sourceProperties; +}MESSAGE_BUFFER_CONFIG; + +/*this creates a new message */ +extern MESSAGE_HANDLE Message_Create(const MESSAGE_CONFIG* cfg); + +/* this creates a new message from a CONSTBUFFER content */ +extern MESSAGE_HANDLE Message_CreateFromBuffer(const MESSAGE_BUFFER_CONFIG* cfg); + +/*this clones a message. Since messages are immutable, it would only increment the inner count*/ +extern MESSAGE_HANDLE Message_Clone(MESSAGE_HANDLE message); + +/*this gets an immutable map (dictionary) of all the properties of the message*/ +extern CONSTMAP_HANDLE Message_GetProperties(MESSAGE_HANDLE message); + +/*this gets the message content*/ +extern const CONSTBUFFER* Message_GetContent(MESSAGE_HANDLE message); + +/*this gets the message content handle*/ +extern const CONSTBUFFER_HANDLE Message_GetContentHandle(MESSAGE_HANDLE message); + +/*this destroys the message*/ +extern void Message_Destroy(MESSAGE_HANDLE message); + +#ifdef __cplusplus +} +#else +#endif + + +#endif /*MESSAGE_H*/ + +``` + +##Message_Create +```C +extern MESSAGE_HANDLE Message_Create(const MESSAGE_CONFIG* cfg); +``` +Message_Create creates a new message from the cfg parameter. +**SRS_MESSAGE_02_002: [**If `cfg` is `NULL` then `Message_Create` shall return `NULL`.**]** +**SRS_MESSAGE_02_003: [**If field `source` of cfg is `NULL` and size is not zero, then `Message_Create` shall fail and return `NULL`.**]** +**SRS_MESSAGE_02_004: [**Mesages shall be allowed to be created from zero-size content.**]** +**SRS_MESSAGE_02_005: [**If `Message_Create` encounters an error while building the internal structures of the message, then it shall return `NULL`.**]** +**SRS_MESSAGE_02_019: [**`Message_Create` shall copy the `sourceProperties` to a readonly CONSTMAP.**]** +**SRS_MESSAGE_17_003: [**`Message_Create` shall copy the `source` to a readonly CONSTBUFFER.**]** +**SRS_MESSAGE_02_006: [**Otherwise, `Message_Create` shall return a non-`NULL` handle and shall set the internal ref count to "1".**]** + + ##Message_CreateFromBuffer + ```C + extern MESSAGE_HANDLE Message_CreateFromBuffer(const MESSAGE_BUFFER_CONFIG* cfg); + ``` + `Message_CreateFromBuffer` creates a new message from a CONSTBUFFER source. + +**SRS_MESSAGE_17_008: [** If `cfg` is `NULL` then `Message_CreateFromBuffer` shall return `NULL`.**]** + **SRS_MESSAGE_17_009: [**If field `sourceContent` of cfg is `NULL`, then `Message_CreateFromBuffer` shall fail and return `NULL`.**]** + **SRS_MESSAGE_17_010: [**If field `sourceProperties` of cfg is `NULL`, then `Message_CreateFromBuffer` shall fail and return `NULL`.**]** + **SRS_MESSAGE_17_011: [**If `Message_CreateFromBuffer` encounters an error while building the internal structures of the message, then it shall return `NULL`.**]** + **SRS_MESSAGE_17_012: [**`Message_CreateFromBuffer` shall copy the `sourceProperties` to a readonly CONSTMAP.**]** + **SRS_MESSAGE_17_013: [**`Message_CreateFromBuffer` shall clone the CONSTBUFFER `sourceBuffer`.**]** + **SRS_MESSAGE_17_014: [**On success, `Message_CreateFromBuffer` shall return a non-`NULL` handle and set the internal ref count to "1".**]** + +##Message_Clone +```C +extern MESSAGE_HANDLE Message_Clone(MESSAGE_HANDLE messageHandle); +``` +Message_Clone creates a clone of the messageHandle. Notice: messages once created are immutable. + +**SRS_MESSAGE_02_007: [**If messageHandle is `NULL` then `Message_Clone` shall return `NULL`.**]** +**SRS_MESSAGE_02_008: [**Otherwise, `Message_Clone` shall increment the internal ref count.**]** +**SRS_MESSAGE_17_001: [**`Message_Clone` shall clone the CONSTMAP handle.**]** +**SRS_MESSAGE_17_004: [**`Message_Clone` shall clone the CONSTBUFFER handle**]** +**SRS_MESSAGE_02_010: [**Message_Clone shall return messageHandle.**]** + +##Message_GetProperties +```C +extern CONSTMAP_HANDLE Message_GetProperties(MESSAGE_HANDLE message); +``` +Message_GetProperties returns a CONSTMAP handle that can be used to access the properties of the message. This handle should be destroyed when no longer needed. + +**SRS_MESSAGE_02_011: [**If message is `NULL` then Message_GetProperties shall return `NULL`.**]** +**SRS_MESSAGE_02_012: [**Otherwise, `Message_GetProperties` shall shall clone and return the CONSTMAP handle representing the properties of the message.**]** + +##Message_GetContent +```C +extern const MESSAGE_CONTENT* Message_GetContent(MESSAGE_HANDLE message) +``` +**SRS_MESSAGE_02_013: [**If message is `NULL` then Message_GetContent shall return `NULL`.**]** +**SRS_MESSAGE_02_014: [**Otherwise, Message_GetContent shall return a non-`NULL` const pointer to a structure of type CONSTBUFFER.**]** +**SRS_MESSAGE_02_015: [**The CONSTBUFFER's field `size` shall have the same value as the cfg's field `size`.**]** +**SRS_MESSAGE_02_016: [**The CONSTBUFFER's field `buffer` shall compare equal byte-by-byte to the cfg's field `source`.**]** +The return of this function needs no free. + +##Message_GetContentHandle +```C +extern CONSTBUFFER_HANDLE Message_GetContentHandle(MESSAGE_HANDLE message); +``` + +This function returns a CONSTBUFFER handle that can be used to access the content. This handle should be destroyed when no longer needed. + +**SRS_MESSAGE_17_006: [**If message is `NULL` then `Message_GetContentHandle` shall return `NULL`.**]** +**SRS_MESSAGE_17_007: [**Otherwise, `Message_GetContentHandle` shall shall clone and return the CONSTBUFFER_HANDLE representing the message content.**]** + +##Message_Destroy(MESSAGE_HANDLE message) +```C +extern void Message_Destroy(MESSAGE_HANDLE message); +``` +**SRS_MESSAGE_02_017: [**If message is `NULL` then `Message_Destroy` shall do nothing.**]** +**SRS_MESSAGE_02_020: [**Otherwise, `Message_Destroy` shall decrement the internal ref count of the message.**]** +**SRS_MESSAGE_17_002: [**`Message_Destroy` shall destroy the CONSTMAP properties.**]** +**SRS_MESSAGE_17_005: [**`Message_Destroy` shall destroy the CONSTBUFFER.**]** +**SRS_MESSAGE_02_021: [**If the ref count is zero then the allocated resources are freed.**]** diff --git a/core/devdoc/module.md b/core/devdoc/module.md new file mode 100644 index 00000000..6aa0ccf5 --- /dev/null +++ b/core/devdoc/module.md @@ -0,0 +1,99 @@ +# Module Requirements + +##Overview +This is the documentation for a module participant to the message bus. +Every module needs to implement the same interface. The implementation of a module is module specific, however, +all modules have the same interface. + +##References + +##Exposed API +```C +#ifndef MODULE_H +#define MODULE_H + +#include "macro_utils.h" +#include "message_bus.h" +#include "message.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*this is the interface that every module on the message bus needs to implement*/ +/*a module can only belong to 1 message bus*/ +/*1 message bus can have (obviously) many modules on it*/ + +typedef void* MODULE_HANDLE; + +/*a module is a pointer to a structure containing several function pointers*/ +/*by convention, every module library exports a function that returns a pointer to that kind of structure*/ +/*that function is called Module_GetAPIs*/ + +/*this API creates a new Module. Configuration is a pointer given by the instantiator*/ +typedef MODULE_HANDLE (*pfModule_Create)(MESSAGE_BUS_HANDLE busHandle, const void* configuration); + +/*this destroys (frees resources) of the module parameter*/ +typedef void (*pfModule_Destroy)(MODULE_HANDLE moduleHandle); + +/*this is the module's callback function - gets called when a message is to be received by the module*/ +typedef void (*pfModule_Receive)(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); + +typedef struct MODULE_APIS_TAG +{ + pfModule_Create Module_Create; + pfModule_Destroy Module_Destroy; + pfModule_Receive Module_Receive; +}MODULE_APIS; + +/*this is the only function exported by a module, under a "by convention" name*/ +/*using the exported function, the called learns of the functions for that module*/ +typedef const MODULE_APIS* (*pfModule_GetAPIS)(void); + +/*return the module APIS*/ +#define MODULE_GETAPIS_NAME ("Module_GetAPIS") + +#ifdef _WIN32 + #define MODULE_EXPORT __declspec(dllexport) +#else + #define MODULE_EXPORT +#endif // _WIN32 + +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void); + +#ifdef __cplusplus +} +#endif + +#endif // MODULE_H + +``` + +##Module_Create +```C +static MODULE_HANDLE Module_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +``` +This function is to be implemented by the module creator. The function receives the MESSAGE +BUS onto which the module will publish its messages, and a pointer provided by the user +containing configuration information (usually information needed by the module to start) + +The function returns a non-`NULL` value when it succeeds, known as the module handle. +If the function fails internally, it should return `NULL`. + +##Module_Destroy +```C +static void Module_Destroy(MODULE_HANDLE moduleHandle); +``` +This function is to be implemented by the module creator. The function receives a previously +created handle by `Module_Create`. If parameter `moduleHandle` is `NULL` the function should +return without taking any action. Otherwise, the function should free all system resources +allocated by the module. + +##Module_Receive +```C +static void Module_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); +``` +This function is to be implemented by the module creator. This function is called by the +framework. This function is not called re-entrant. This function shouldn't assume it is +called from the same thread. \ No newline at end of file diff --git a/core/devdoc/module_loader_requirements.md b/core/devdoc/module_loader_requirements.md new file mode 100644 index 00000000..d6ba89a6 --- /dev/null +++ b/core/devdoc/module_loader_requirements.md @@ -0,0 +1,55 @@ +# module_loader Requirements + + +  +## Overview +module_loader allows a user to dynamically load modules into a gateway. The module in this case is represented by a file name of a shared library (a DLL or SO file, depending on operating system). The library name given is expected to implement a gateway module, so it is expected to export the API as defined in module.h. +## References +module.h – defines `MODULE_APIS`, `MODULE_GETAPIS_NAME`, and `Module_GetAPIS` function declaration. +## Exposed API +```C +typedef struct MODULE_LIBRARY_DATA_TAG* MODULE_LIBRARY_HANDLE; + +extern MODULE_LIBRARY_HANDLE ModuleLoader_Load(const char* moduleLibraryFileName); +extern const MODULE_APIS* ModuleLoader_GetModuleAPIs(MODULE_LIBRARY_HANDLE moduleLibraryHandle); +extern void ModuleLoader_Unload(MODULE_LIBRARY_HANDLE moduleLibraryHandle); +``` + +### ModuleLoader_Load +```C +extern MODULE_LIBRARY_HANDLE ModuleLoader_Load(const char* moduleLibraryFileName); +``` + +**SRS_MODULE_LOADER_17_001: [**`ModuleLoader_Load` shall validate the moduleLibraryFileName, if it is `NULL`, it shall return NULL.**]** + +**SRS_MODULE_LOADER_17_002: [**`ModuleLoader_Load` shall load the library as a file, the filename given by the moduleLibraryFileName.**]** **SRS_MODULE_LOADER_17_012: [**If load library is not successful, the load shall fail, and it shall return `NULL`.**]** + +**SRS_MODULE_LOADER_17_003: [**`ModuleLoader_Load` shall locate the function defined by `MODULE_GETAPIS_NAME` in the open library.**]** **SRS_MODULE_LOADER_17_013: [**If locating the function is not successful, the load shall fail, and it shall return `NULL`.**]** + +**SRS_MODULE_LOADER_17_004: [**`ModuleLoader_Load` shall call the function defined by `MODULE_GETAPIS_NAME` in the open library.**]** **SRS_MODULE_LOADER_17_015: [**If the get API call returns `NULL`, the load shall fail, and it shall return `NULL`.**]** + +**SRS_MODULE_LOADER_17_005: [**`ModuleLoader_Load` shall allocate memory for the structure `MODULE_LIBRARY_HANDLE`.**]** **SRS_MODULE_LOADER_17_014: [**If memory allocation is not successful, the load shall fail, and it shall return `NULL`.**]** + +**SRS_MODULE_LOADER_17_006: [**`ModuleLoader_Load` shall return a non-NULL handle to a `MODULE_LIBRARY_DATA_TAG` upon success.**]** + +The contents of the structure `MODULE_LIBRARY_DATA_TAG` will be operating system specific. The structure is expected to have at least one element: an opaque handle to the loaded library. The structure may also to keep a reference to the `MODULE_APIS` provided by the library to improve performance. + +### ModuleLoader_GetModuleAPIs +```C +extern const MODULE_APIS* `ModuleLoader_GetModuleAPIs`(MODULE_LIBRARY_HANDLE moduleLibraryHandle); +``` + +**SRS_MODULE_LOADER_17_007: [**`ModuleLoader_GetModuleAPIs` shall return `NULL` if the moduleLibraryHandle is `NULL`.**]** + +**SRS_MODULE_LOADER_17_008: [**`ModuleLoader_GetModuleAPIs` shall return a valid pointer to `MODULE_APIS` on success.**]** + +### ModuleLoader_Unload +```C +extern void ModuleLoader_Unload(MODULE_LIBRARY_HANDLE moduleLibraryHandle); +``` + +**SRS_MODULE_LOADER_17_009: [**`ModuleLoader_Unload` shall do nothing if the moduleLibraryHandle is `NULL`.**]** + +**SRS_MODULE_LOADER_17_010: [**`ModuleLoader_Unload` shall unload the library.**]** + +**SRS_MODULE_LOADER_17_011: [**`ModuleLoader_Unload` shall deallocate memory for the structure `MODULE_LIBRARY_HANDLE`.**]** diff --git a/core/inc/dynamic_library.h b/core/inc/dynamic_library.h new file mode 100644 index 00000000..c6f3090e --- /dev/null +++ b/core/inc/dynamic_library.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef DYNAMIC_LIBRARY_H +#define DYNAMIC_LIBRARY_H + + +#ifdef __cplusplus +extern "C" +{ +#endif +typedef void* DYNAMIC_LIBRARY_HANDLE; + +extern DYNAMIC_LIBRARY_HANDLE DynamicLibrary_LoadLibrary(const char* dynamicLibraryFileName); +extern void DynamicLibrary_UnloadLibrary(DYNAMIC_LIBRARY_HANDLE libraryHandle); +extern void* DynamicLibrary_FindSymbol(DYNAMIC_LIBRARY_HANDLE libraryHandle, const char* symbolName); + +#ifdef __cplusplus +} +#endif + +#endif // DYNAMIC_LIBRARY_H diff --git a/core/inc/gateway.h b/core/inc/gateway.h new file mode 100644 index 00000000..7fb30dd2 --- /dev/null +++ b/core/inc/gateway.h @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file gateway.h +* @brief Extends the Gateway_LL library with additional features. +* +* @details Gateway extends the Gateway_LL library with 1 additional +* feature: +* - Creating a gateway from a JSON configuration file. +*/ + +#ifndef GATEWAY_H +#define GATEWAY_H + +#include "gateway_ll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief Creates a Gateway using a JSON configuration file as input which + * describes each module. + * + * @param file_path Path to the JSON configuration file for this gateway. + * Sample JSON configuration file: + * + * { + * "modules" : + * [ + * { + * "module name" : "foo", + * "module path" : "F:\\foo.dll", + * "args" : ... + * }, + * { + * "module name" : "bar", + * "module path" : "F:\\bar.dll", + * "args" : ... + * } + * ] + * } + * + * @return A non-NULL #GATEWAY_HANDLE that can be used to manage the + * gateway or @c NULL on failure. + */ + extern GATEWAY_HANDLE Gateway_Create_From_JSON(const char* file_path); + +#ifdef __cplusplus +} +#endif + +#endif // GATEWAY_H diff --git a/core/inc/gateway_ll.h b/core/inc/gateway_ll.h new file mode 100644 index 00000000..e449df9b --- /dev/null +++ b/core/inc/gateway_ll.h @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file gateway_ll.h +* @brief Library that allows a user to create and configure a gateway. +* +* @details Gateway LL is the lower level library that allows a developer to +* create, configure, and manage a gateway. The library provides a +* mechanism for creating and destroying a gateway, as well as +* adding and removing modules. Developers looking for the high +* level library should see gateway.h. +*/ + +#ifndef GATEWAY_LL_H +#define GATEWAY_LL_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/vector.h" +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** @brief Struct representing a particular gateway. */ +typedef struct GATEWAY_HANDLE_DATA_TAG* GATEWAY_HANDLE; + +/** @brief Struct representing a single entry of the #GATEWAY_PROPERTIES. */ +typedef struct GATEWAY_PROPERTIES_ENTRY_TAG +{ + /** @brief The (possibly @c NULL) name of the module */ + const char* module_name; + + /** @brief The path to the .dll or .so of the module */ + const char* module_path; + + /** @brief The user-defined configuration object for the module */ + const void* module_configuration; +} GATEWAY_PROPERTIES_ENTRY; + +/** @brief Struct representing the properties that should be used when + creating a module; each entry of the @c VECTOR_HANDLE being a +* #GATEWAY_PROPERTIES_ENTRY. +*/ +typedef struct GATEWAY_PROPERTIES_DATA_TAG +{ + /** @brief Vector of #GATEWAY_PROPERTIES_ENTRY objects. */ + VECTOR_HANDLE gateway_properties_entries; +} GATEWAY_PROPERTIES; + +/** @brief Creates a new gateway using the provided #GATEWAY_PROPERTIES. +* +* @param properties #GATEWAY_PROPERTIES structure containing +* specific module properties and information. +* +* @return A non-NULL #GATEWAY_HANDLE that can be used to manage the +* gateway or @c NULL on failure. +*/ +extern GATEWAY_HANDLE Gateway_LL_Create(const GATEWAY_PROPERTIES* properties); + +/** @brief Destroys the gateway and disposes of all associated data. +* +* @param gw #GATEWAY_HANDLE to be destroyed. +*/ +extern void Gateway_LL_Destroy(GATEWAY_HANDLE gw); + +/** @brief Creates a new module based on the GATEWAY_PROPERTIES_ENTRY*. +* +* @param gw Pointer to a #GATEWAY_HANDLE to add the Module onto. +* @param entry Pointer to a #GATEWAY_PROPERTIES_ENTRY structure +* describing the module. +* +* @return A non-NULL #MODULE_HANDLE to the newly created and added +* Module, or @c NULL on failure. +*/ +extern MODULE_HANDLE Gateway_LL_AddModule(GATEWAY_HANDLE gw, const GATEWAY_PROPERTIES_ENTRY* entry); + +/** @brief Removes the provided module from the gateway. +* +* @param gw Pointer to a #GATEWAY_HANDLE from which to remove the +* Module. +* @param module Pointer to a #MODULE_HANDLE that needs to be removed. +*/ +extern void Gateway_LL_RemoveModule(GATEWAY_HANDLE gw, MODULE_HANDLE module); + +#ifdef __cplusplus +} +#endif + +#endif // GATEWAY_LL_H diff --git a/core/inc/linux/gb_library.h b/core/inc/linux/gb_library.h new file mode 100644 index 00000000..bf96f5ca --- /dev/null +++ b/core/inc/linux/gb_library.h @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#ifndef GB_LIBRARY_H +#define GB_LIBRARY_H + +/*this file, if included instead of has the following functionality: +1) if GB_LIBRARY_INTERCEPT is defined then +a) some of the dlfcn.h symbols shall be redefined: dlopen, dlclose and dlsym +b) all "code" using the the above functions will actually (because of the preprocessor) call to gb_ equivalent +c) gb_ shall blindly call into , thus realizing a passthrough + +reason is: unittesting. dl functions come with the C Run Time and cannot be mocked +(that is, in the global namespace cannot exist a function called dlopen) + +2) if GB_LIBRARY_INTERCEPT is not defined then +a) it shall include => no passthrough, just direct linking. +*/ + +#ifndef GB_LIBRARY_INTERCEPT +#include +#else + +/*a)source level intercepting of function calls +This causes the C Compiler in the include below () to declare +the kernel function as _never_called_never_implemented_always_forgotten +so we are free to define our own for testing */ +#define dlopen dlopen_never_called_never_implemented_always_forgotten +#define dlclose dlclose_never_called_never_implemented_always_forgotten +#define dlsym dlsym_never_called_never_implemented_always_forgotten + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*b) call to gb_ equivalent */ +#undef dlopen +#define dlopen gb_dlopen +extern void *gb_dlopen(const char *__file, int __mode); + + +#undef dlclose +#define dlclose gb_dlclose +extern int gb_dlclose(void *__handle); + +#undef dlsym +#define dlsym gb_dlsym +extern void *gb_dlsym(void * __handle, const char * __name); + + +#ifdef __cplusplus +} +#endif + +#endif /* GB_LIBRARY_INTERCEPT*/ + +#endif /* !GB_LIBRARY_H */ diff --git a/core/inc/message.h b/core/inc/message.h new file mode 100644 index 00000000..e60cbf98 --- /dev/null +++ b/core/inc/message.h @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file message.h +* +* @brief Defines structures and function prototypes for creating, +* inspecting and disposing off messages published on to the +* message bus. +* +* @details A message essentially has two components: +* - Properties represented as key/value pairs where both the +* key and value are strings +* - The content of the message which is simply a memory buffer +* (a @c BUFFER_HANDLE) +* +* This header provides functions that enable the creation, inspection +* and destruction of messages. A message is represented via a +* #MESSAGE_HANDLE which is an opaque reference to a message +* instance. Messages are immutable and reference counted; their +* lifetime is managed via the #Message_Clone and #Message_Destroy +* APIs which atomically increment and decrement the reference +* count respectively. #Message_Destroy deallocates the message +* completely when the reference count becomes zero. +*/ + +#ifndef MESSAGE_H +#define MESSAGE_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/constmap.h" +#include "azure_c_shared_utility/constbuffer.h" + +#ifdef __cplusplus +#include +#include +extern "C" +{ +#else +#include +#include +#endif + +/** @brief Struct representing a particular message. */ +typedef struct MESSAGE_HANDLE_DATA_TAG* MESSAGE_HANDLE; + +/** @brief Struct defining the Message configuration; messages are constructed +* using this structure. +*/ +typedef struct MESSAGE_CONFIG_TAG +{ + /** @brief Specifies the size of the buffer pointed at by @c source. This + * can be zero when the message has only properties and no content. + * It is an error for the size to be greater than zero when @c + * source is equal to @c NULL. + */ + size_t size; + + /** @brief Pointer to the buffer containing the data that will be the + * content of this message. This can be @c NULL when @c size is + * zero. + */ + const unsigned char* source; + + /** @brief A collection of key/value pairs where both the key and value are + * strings representing the properties of this message. This field + * must not be @c NULL. + */ + MAP_HANDLE sourceProperties; +}MESSAGE_CONFIG; + +/** @brief Struct defining the Message buffer configuration. */ +typedef struct MESSAGE_BUFFER_CONFIG_TAG +{ + /** @brief The buffer containing the data that will be the content of the + * message. + */ + CONSTBUFFER_HANDLE sourceContent; + + /** @brief A collection of key/value pairs where both the key and value are + * strings representing the properties of this message. This field + * must not be @c NULL. + */ + MAP_HANDLE sourceProperties; +}MESSAGE_BUFFER_CONFIG; + +/** @brief Creates a new reference counted message from a #MESSAGE_CONFIG +* structure with the reference count initialized to 1. +* +* @details This function will create its own @c CONSTBUFFER and @c ConstMap +* with copies of the @c source and @c sourceProperties contained +* within the #MESSAGE_CONFIG structure parameter. It is the +* responsibility of the Message to dispose of these resources. +* +* @param cfg Pointer to a #MESSAGE_CONFIG structure. +* +* @return A non-NULL #MESSAGE_HANDLE for the newly created message, or NULL +* upon failure. +*/ +extern MESSAGE_HANDLE Message_Create(const MESSAGE_CONFIG* cfg); + +/** @brief Creates a new message from a @c CONSTBUFFER source and @c MAP_HANDLE. +* +* @details This function will create its own @c CONSTBUFFER and @c ConstMap +* with copies of the @c source and @c sourceProperties contained +* within the #MESSAGE_CONFIG structure parameter. It is the +* responsibility of the Message to dispose of these resources. +* +* @param cfg Pointer to a #MESSAGE_BUFFER_CONFIG structure. +* +* @return A non-NULL #MESSAGE_HANDLE for the newly created message, or @c NULL +* upon failure. +*/ +extern MESSAGE_HANDLE Message_CreateFromBuffer(const MESSAGE_BUFFER_CONFIG* cfg); + +/** @brief Creates a clone of the message. +* +* @details Since messages are immutable, this function only increments the inner + reference count. +* +* @param message The #MESSAGE_HANDLE that will be cloned. +* +* @return A non-NULL #MESSAGE_HANDLE cloned from @c message, or @c NULL upon +* failure. +*/ +extern MESSAGE_HANDLE Message_Clone(MESSAGE_HANDLE message); + +/** @brief Gets the properties of a message. +* +* @details The returned @c CONSTMAP handle should be destroyed when no longer +* needed. +* +* @param message The #MESSAGE_HANDLE from which properties will be +* fetched. +* +* @return A non-NULL @c CONSTMAP_HANDLE representing the properties of the +* message, or @c NULL upon failure. +* +*/ +extern CONSTMAP_HANDLE Message_GetProperties(MESSAGE_HANDLE message); + +/** @brief Gets the content of a message. +* +* @details The returned @c CONSTMAP_HANDLE need not be freed by the caller. +* +* @param message The #MESSAGE_HANDLE from which the content will be +* fetched. +* +* @return A non-NULL pointer to a @c CONSTBUFFER representing the content of +* the message, or @c NULL upon failure. +*/ +extern const CONSTBUFFER* Message_GetContent(MESSAGE_HANDLE message); + +/** @brief Gets the @c CONSTBUFFER handle that may be used to access the message +* content. +* +* @details This handle must be destroyed when no longer needed. +* +* @param message The #MESSAGE_HANDLE from which the content will be +* fetched. +* +* @return A non-NULL @c CONSTBUFFER_HANDLE representing the message content, or +* @c NULL upon failure. +*/ +extern CONSTBUFFER_HANDLE Message_GetContentHandle(MESSAGE_HANDLE message); + +/** @brief Disposes of resources allocated by the message. +* +* @param message The #MESSAGE_HANDLE to be destroyed. +*/ +extern void Message_Destroy(MESSAGE_HANDLE message); + +#ifdef __cplusplus +} +#else +#endif + +#endif /*MESSAGE_H*/ diff --git a/core/inc/message_bus.h b/core/inc/message_bus.h new file mode 100644 index 00000000..37153b44 --- /dev/null +++ b/core/inc/message_bus.h @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file message_bus.h +* @brief Library for configuring and using the gateway's message bus. +* +* @details This is the API to create a reference counted and thread safe +* gateway message bus. The message bus broadcasts the messages to +* the subscribers. Messages on the message bus have a bag of +* properties (name, value) and an opaque array of bytes that is +* the message content. +*/ + +#ifndef MESSAGE_BUS_H +#define MESSAGE_BUS_H + +/** @brief Struct representing a particular message bus. */ +typedef struct MESSAGE_BUS_HANDLE_DATA_TAG* MESSAGE_BUS_HANDLE; + +#include "azure_c_shared_utility/macro_utils.h" +#include "message.h" +#include "module.h" + +#ifdef __cplusplus +#include +extern "C" +{ +#else +#include +#endif + +#define MESSAGE_BUS_RESULT_VALUES \ + MESSAGE_BUS_OK, \ + MESSAGE_BUS_ERROR, \ + MESSAGE_BUS_INVALIDARG + +/** @brief Enumeration describing the result of ::MessageBus_Publish, +* ::MessageBus_AddModule, and ::MessageBus_RemoveModule. +*/ +DEFINE_ENUM(MESSAGE_BUS_RESULT, MESSAGE_BUS_RESULT_VALUES); + +/** @brief Creates a new message bus. +* +* @return A valid #MESSAGE_BUS_HANDLE upon success, or @c NULL upon failure. +*/ +extern MESSAGE_BUS_HANDLE MessageBus_Create(void); + +/** @brief Creates a clone of the message bus. +* +* @details This function will simply increment the internal reference +* count of the provided #MESSAGE_BUS_HANDLE. +* +* @param bus The #MESSAGE_BUS_HANDLE to be cloned. +*/ +extern void MessageBus_IncRef(MESSAGE_BUS_HANDLE bus); + +/** @brief Decrements the reference count of a message bus. +* +* @details This function will simply decrement the internal reference +* count of the provided #MESSAGE_BUS_HANDLE, destroying the message +* bus when the reference count reaches 0. +* +* @param bus The #MESSAGE_BUS_HANDLE whose ref count will be decremented. +*/ +extern void MessageBus_DecRef(MESSAGE_BUS_HANDLE bus); + +/** @brief Publishes a message onto the message bus. +* +* @details For details about threading with regard to the message bus and +* modules connected to the message bus, see +* Bus High Level Design Documentation. +* +* @param bus The #MESSAGE_BUS_HANDLE onto which the message will be +* published. +* @param message The #MESSAGE_HANDLE representing the message to be +* published. +* +* @return A #MESSAGE_BUS_RESULT describing the result of the function. +*/ +extern MESSAGE_BUS_RESULT MessageBus_Publish(MESSAGE_BUS_HANDLE bus, MESSAGE_HANDLE message); + +/** @brief Adds a module onto the message bus. +* +* @details For details about threading with regard to the message bus and +* modules connected to the message bus, see +* Bus High Level Design Documentation. +* +* @param bus The #MESSAGE_BUS_HANDLE onto which the module will be +* added. +* @param module The #MODULE_HANDLE for the module that will be added +* to this message bus. +* @param module_apis The #MODULE_APIS containing the corresponding functions +* for the module that will be added. +* +* @return A #MESSAGE_BUS_RESULT describing the result of the function. +*/ +extern MESSAGE_BUS_RESULT MessageBus_AddModule(MESSAGE_BUS_HANDLE bus, MODULE_HANDLE module, const MODULE_APIS* module_apis); + +/** @brief Removes a module from the message bus. +* +* @param bus The #MESSAGE_BUS_HANDLE from which the module will be removed. +* @param module The #MODULE_HANDLE of the module to be removed. +* +* @return A #MESSAGE_BUS_RESULT describing the result of the function. +*/ +extern MESSAGE_BUS_RESULT MessageBus_RemoveModule(MESSAGE_BUS_HANDLE bus, MODULE_HANDLE module); + +/** @brief Disposes of resources allocated by a message bus. +* +* @param bus The #MESSAGE_BUS_HANDLE to be destroyed. +*/ +extern void MessageBus_Destroy(MESSAGE_BUS_HANDLE bus); + +// This variable is used only for unit testing purposes. +extern size_t BUS_offsetof_quit_worker; + +#ifdef __cplusplus +} +#endif + + +#endif /*MESSAGE_BUS_H*/ diff --git a/core/inc/module.h b/core/inc/module.h new file mode 100644 index 00000000..3717c244 --- /dev/null +++ b/core/inc/module.h @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file module.h +* @brief Interface for modules which connect to a message bus. +* +* @details Every module on the message bus must implement this interface. +* A module can only belong to 1 message bus and a message bus may +* have many modules connected to it. +* +* A module is a pointer to a structure containing several function +* pointers. By convention, every module exports a function that +* returns a pointer to an instance of the #MODULE_APIS structure.. +*/ + +#ifndef MODULE_H +#define MODULE_H + +/** @brief Represents a handle to a particular module.*/ +typedef void* MODULE_HANDLE; +typedef struct MODULE_APIS_TAG MODULE_APIS; + +#include "azure_c_shared_utility/macro_utils.h" +#include "message_bus.h" +#include "message.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** @brief Creates a module using the specified configuration connecting + * to the specified message bus. + * + * @details This function is to be implemented by the module creator. + * + * @param busHandle The #MESSAGE_BUS_HANDLE onto which this module + * will connect. + * @param configureation A pointer to the user-defined configuration + * structure for this module. + * + * @return A non-NULL #MODULE_HANDLE upon success, or @c NULL upon + * failure. + */ + typedef MODULE_HANDLE(*pfModule_Create)(MESSAGE_BUS_HANDLE busHandle, const void* configuration); + + /** @brief Disposes of the resources allocated by/for this module. + * + * @details This function is to be implemented by the module creator. + * + * @param moduleHandle The #MODULE_HANDLE of the module to be destroyed. + */ + typedef void(*pfModule_Destroy)(MODULE_HANDLE moduleHandle); + + /** @brief The module's callback function that is called upon message + * receipt. + * + * @details This function is to be implemented by the module creator. + * + * @param moduleHandle The #MODULE_HANDLE of the module receiving the + * message. + * @param messageHandle The #MESSAGE_HANDLE of the message being sent + * to the module. + */ + typedef void(*pfModule_Receive)(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); + + /** @brief Structure returned by ::Module_GetAPIS containing the function + * pointers of the module-specific implementations of the interface. + */ + typedef struct MODULE_APIS_TAG + { + /** @brief Function pointer to the #Module_Create function. */ + pfModule_Create Module_Create; + + /** @brief Function pointer to the #Module_Destroy function. */ + pfModule_Destroy Module_Destroy; + + /** @brief Function pointer to the #Module_Receive function. */ + pfModule_Receive Module_Receive; + }MODULE_APIS; + + /** @brief This is the only function exported by a module. Using the + * exported function, the caller learns the functions for the + * particular module. + */ + typedef const MODULE_APIS* (*pfModule_GetAPIS)(void); + + /** @brief Returns the module APIS name.*/ +#define MODULE_GETAPIS_NAME ("Module_GetAPIS") + +#ifdef _WIN32 +#define MODULE_EXPORT __declspec(dllexport) +#else +#define MODULE_EXPORT +#endif // _WIN32 + +/** @brief Forms a new name that is by convention the name of the (only) exported +* function from a static module library. +*/ +#define MODULE_STATIC_GETAPIS(MODULE_NAME) C2(Module_GetAPIS_, MODULE_NAME) + +/** @brief This is the only function exported by a module under a "by +* convention" name. Using the exported function, the caller learns +* the functions for the particular module. +*/ +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void); + +#ifdef __cplusplus +} +#endif + +#endif // MODULE_H diff --git a/core/inc/module_config_resources.h b/core/inc/module_config_resources.h new file mode 100644 index 00000000..1d8a3934 --- /dev/null +++ b/core/inc/module_config_resources.h @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MODULE_CONFIG_RESOURCES_H +#define MODULE_CONFIG_RESOURCES_H + +extern "C" { +//Add here paths for any new Module. +const char* e2e_module_path(); +const char* iothubhttp_module_path(); +const char* identity_map_module_path(); +} + +#endif /*MODULE_CONFIG_RESOURCES_H*/ \ No newline at end of file diff --git a/core/inc/module_loader.h b/core/inc/module_loader.h new file mode 100644 index 00000000..d06e29cd --- /dev/null +++ b/core/inc/module_loader.h @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MODULE_LOADER_H +#define MODULE_LOADER_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct MODULE_LIBRARY_HANDLE_DATA_TAG* MODULE_LIBRARY_HANDLE; + +extern MODULE_LIBRARY_HANDLE ModuleLoader_Load(const char* moduleLibraryFileName); +extern const MODULE_APIS* ModuleLoader_GetModuleAPIs(MODULE_LIBRARY_HANDLE moduleLibraryHandle); +extern void ModuleLoader_Unload(MODULE_LIBRARY_HANDLE moduleLibraryHandle); + +#ifdef __cplusplus +} +#endif + +#endif // MODULE_LOADER_H diff --git a/core/inc/windows/gb_library.h b/core/inc/windows/gb_library.h new file mode 100644 index 00000000..bfd55fe2 --- /dev/null +++ b/core/inc/windows/gb_library.h @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#ifndef GB_LIBRARY_H +#define GB_LIBRARY_H + +/*this file, if included instead of has the following functionality: +1) if GB_LIBRARY_INTERCEPT is defined then +a) some of the dlfcn.h symbols shall be redefined: LoadLibraryA, FreeLibrary and GetProcAddress +b) all "code" using the the above functions will actually (because of the preprocessor) call to gb_ equivalent +c) gb_ shall blindly call into , thus realizing a passthrough + +reason is: unittesting. DLL functions come with the C Run Time and cannot be mocked +(that is, in the global namespace cannot exist a function called LoadLibraryA) + +2) if GB_LIBRARY_INTERCEPT is not defined then +a) it shall include => no passthrough, just direct linking. +*/ + +#ifndef GB_LIBRARY_INTERCEPT +#include +#else + +/*a)source level intercepting of function calls +This causes the C Compiler in the include below () to declare +the kernel function as _never_called_never_implemented_always_forgotten +so we are free to define our own for testing +*/ +#define LoadLibraryA LoadLibraryA_never_called_never_implemented_always_forgotten +#define FreeLibrary FreeLibrary_never_called_never_implemented_always_forgotten +#define GetProcAddress GetProcAddress_never_called_never_implemented_always_forgotten +#define GetLastError GetLastError_never_called_never_implemented_always_forgotten +#define GetCurrentDirectoryA GetCurrentDirectoryA_never_called_never_implemented_always_forgotten + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*b) call to gb_ equivalent */ + +#undef LoadLibraryA +#define LoadLibraryA gb_LoadLibraryA +extern void* gb_LoadLibraryA(const char* dynamicLibraryFileName); + +#undef FreeLibrary +#define FreeLibrary gb_FreeLibrary +extern int gb_FreeLibrary(void* library); + +#undef GetProcAddress +#define GetProcAddress gb_GetProcAddress +extern void* gb_GetProcAddress(void* library, const char* symbolName); + +#undef GetLastError +#define GetLastError gb_GetLastError +extern DWORD gb_GetLastError(); + +#undef GetCurrentDirectoryA +#define GetCurrentDirectoryA gb_GetCurrentDirectoryA +extern DWORD gb_GetCurrentDirectoryA(DWORD nBufferLength, char* lpBuffer ); + + + + +#ifdef __cplusplus +} +#endif + +#endif /* GB_LIBRARY_INTERCEPT*/ + +#endif /* !GB_LIBRARY_H */ diff --git a/core/src/gateway.c b/core/src/gateway.c new file mode 100644 index 00000000..bd60dd1b --- /dev/null +++ b/core/src/gateway.c @@ -0,0 +1,180 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/macro_utils.h" +#include "gateway.h" +#include "parson.h" + +#define MODULES_KEY "modules" +#define MODULE_NAME_KEY "module name" +#define MODULE_PATH_KEY "module path" +#define ARG_KEY "args" + +#define PARSE_JSON_RESULT_VALUES \ + PARSE_JSON_SUCCESS, \ + PARSE_JSON_FAILURE, \ + PARSE_JSON_MISSING_OR_MISCONFIGURED_CONFIG, \ + PARSE_JSON_VECTOR_FAILURE, \ + PARSE_JSON_MISCONFIGURED_OR_OTHER + +DEFINE_ENUM(PARSE_JSON_RESULT, PARSE_JSON_RESULT_VALUES); + +static PARSE_JSON_RESULT parse_json_internal(GATEWAY_PROPERTIES* out_properties, JSON_Value *root); +static void destroy_properties_internal(GATEWAY_PROPERTIES* properties); + +GATEWAY_HANDLE Gateway_Create_From_JSON(const char* file_path) +{ + GATEWAY_HANDLE gw; + + if (file_path != NULL) + { + JSON_Value *root_value; + + /*Codes_SRS_GATEWAY_14_002: [The function shall use parson to read the file and parse the JSON string to a parson JSON_Value structure.]*/ + root_value = json_parse_file(file_path); + if (root_value != NULL) + { + /*Codes_SRS_GATEWAY_14_004: [The function shall traverse the JSON_Value object to initialize a GATEWAY_PROPERTIES instance.]*/ + GATEWAY_PROPERTIES *properties = (GATEWAY_PROPERTIES*)malloc(sizeof(GATEWAY_PROPERTIES)); + + if (properties != NULL) + { + if (parse_json_internal(properties, root_value) == PARSE_JSON_SUCCESS) + { + /*Codes_SRS_GATEWAY_14_007: [The function shall use the GATEWAY_PROPERTIES instance to create and return a GATEWAY_HANDLE using the lower level API.]*/ + gw = Gateway_LL_Create(properties); + + if (gw == NULL) + { + LogError("Failed to create gateway using lower level library."); + } + destroy_properties_internal(properties); + } + /*Codes_SRS_GATEWAY_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ + else + { + gw = NULL; + LogError("Failed to create properties structure from JSON configuration."); + } + + free(properties); + } + /*Codes_SRS_GATEWAY_14_008: [This function shall return NULL upon any memory allocation failure.]*/ + else + { + gw = NULL; + LogError("Malloc failed."); + } + + json_value_free(root_value); + } + else + { + /*Codes_SRS_GATEWAY_14_003: [The function shall return NULL if the file contents could not be read and / or parsed to a JSON_Value.]*/ + gw = NULL; + LogError("Input file [%s] could not be read.", file_path); + } + } + /*Codes_SRS_GATEWAY_14_001: [If file_path is NULL the function shall return NULL.]*/ + else + { + gw = NULL; + LogError("Input file path is NULL."); + } + + return gw; +} + +static void destroy_properties_internal(GATEWAY_PROPERTIES* properties) +{ + size_t vector_size = VECTOR_size(properties->gateway_properties_entries); + for (size_t element_index = 0; element_index < vector_size; ++element_index) + { + GATEWAY_PROPERTIES_ENTRY* element = (GATEWAY_PROPERTIES_ENTRY*)VECTOR_element(properties->gateway_properties_entries, element_index); + json_free_serialized_string((char*)(element->module_configuration)); + } + + VECTOR_destroy(properties->gateway_properties_entries); +} + +static PARSE_JSON_RESULT parse_json_internal(GATEWAY_PROPERTIES* out_properties, JSON_Value *root) +{ + PARSE_JSON_RESULT result; + + JSON_Object *modules_object = json_value_get_object(root); + if (modules_object != NULL) + { + JSON_Array *modules_array = json_object_get_array(modules_object, MODULES_KEY); + + if (modules_array != NULL) + { + out_properties->gateway_properties_entries = VECTOR_create(sizeof(GATEWAY_PROPERTIES_ENTRY)); + if (out_properties->gateway_properties_entries != NULL) + { + JSON_Object *module; + size_t module_count = json_array_get_count(modules_array); + for (size_t module_index = 0; module_index < module_count; ++module_index) + { + module = json_array_get_object(modules_array, module_index); + const char* module_name = json_object_get_string(module, MODULE_NAME_KEY); + const char* module_path = json_object_get_string(module, MODULE_PATH_KEY); + + if (module_name != NULL && module_path != NULL) + { + /*Codes_SRS_GATEWAY_14_005: [The function shall set the value of const void* module_properties in the GATEWAY_PROPERTIES instance to a char* representing the serialized args value for the particular module.]*/ + JSON_Value *args = json_object_get_value(module, ARG_KEY); + char* args_str = json_serialize_to_string(args); + + GATEWAY_PROPERTIES_ENTRY entry = { + module_name, + module_path, + args_str + }; + + /*Codes_SRS_GATEWAY_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ + if (VECTOR_push_back(out_properties->gateway_properties_entries, &entry, 1) == 0) + { + result = PARSE_JSON_SUCCESS; + } + else + { + json_free_serialized_string(args_str); + destroy_properties_internal(out_properties); + result = PARSE_JSON_VECTOR_FAILURE; + LogError("Failed to push data into properties vector."); + break; + } + } + /*Codes_SRS_GATEWAY_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ + else + { + destroy_properties_internal(out_properties); + result = PARSE_JSON_MISSING_OR_MISCONFIGURED_CONFIG; + LogError("\"module name\" or \"module path\" in input JSON configuration is missing or misconfigured."); + break; + } + } + } + else + { + result = PARSE_JSON_VECTOR_FAILURE; + LogError("Failed to create properties vector. "); + } + } + /*Codes_SRS_GATEWAY_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ + else + { + result = PARSE_JSON_MISCONFIGURED_OR_OTHER; + LogError("JSON Configuration file is configured incorrectly or some other error occurred while parsing."); + } + } + /*Codes_SRS_GATEWAY_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ + else + { + result = PARSE_JSON_MISCONFIGURED_OR_OTHER; + LogError("JSON Configuration file is configured incorrectly or some other error occurred while parsing."); + } + return result; +} diff --git a/core/src/gateway_ll.c b/core/src/gateway_ll.c new file mode 100644 index 00000000..87a9f603 --- /dev/null +++ b/core/src/gateway_ll.c @@ -0,0 +1,295 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" + +#include + +#include "gateway_ll.h" +#include "message_bus.h" +#include "module_loader.h" + +typedef struct GATEWAY_HANDLE_DATA_TAG { + /** @brief Vector of MODULE_DATA modules that the Gateway must track */ + VECTOR_HANDLE modules; + + /** @brief The message bus contained within this Gateway */ + MESSAGE_BUS_HANDLE bus; +} GATEWAY_HANDLE_DATA; + +typedef struct MODULE_DATA_TAG { + /** @brief The MODULE_LIBRARY_HANDLE associated with 'module'*/ + MODULE_LIBRARY_HANDLE module_library_handle; + + /** @brief The MODULE_HANDLE of the same module that lives on the message bus.*/ + MODULE_HANDLE module; +} MODULE_DATA; + +static MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE gw, const char* module_path, const void* module_configuration); +static void gateway_removemodule_internal(GATEWAY_HANDLE gw, MODULE_DATA* module); +static bool module_data_find(const void* element, const void* value); + +GATEWAY_HANDLE Gateway_LL_Create(const GATEWAY_PROPERTIES* properties) +{ + /*Codes_SRS_GATEWAY_LL_14_001: [This function shall create a GATEWAY_HANDLE representing the newly created gateway.]*/ + GATEWAY_HANDLE_DATA* gateway = (GATEWAY_HANDLE_DATA*)malloc(sizeof(GATEWAY_HANDLE_DATA)); + if (gateway != NULL) + { + /*Codes_SRS_GATEWAY_LL_14_003: [This function shall create a new MESSAGE_BUS_HANDLE for the gateway representing this gateway's message bus. ]*/ + gateway->bus = MessageBus_Create(); + if (gateway->bus == NULL) + { + /*Codes_SRS_GATEWAY_LL_14_004: [This function shall return NULL if a MESSAGE_BUS_HANDLE cannot be created.]*/ + free(gateway); + gateway = NULL; + LogError("Gateway_LL_Create(): MessageBus_Create() failed."); + } + else + { + /*Codes_SRS_GATEWAY_LL_14_033: [ The function shall create a vector to store each MODULE_DATA. ]*/ + gateway->modules = VECTOR_create(sizeof(MODULE_DATA)); + if (gateway->modules == NULL) + { + /*Codes_SRS_GATEWAY_LL_14_034: [ This function shall return NULL if a VECTOR_HANDLE cannot be created. ]*/ + /*Codes_SRS_GATEWAY_LL_14_035: [ This function shall destroy the previously created MESSAGE_BUS_HANDLE and free the GATEWAY_HANDLE if the VECTOR_HANDLE cannot be created. ]*/ + MessageBus_Destroy(gateway->bus); + free(gateway); + gateway = NULL; + LogError("Gateway_LL_Create(): VECTOR_create failed."); + } + else + { + if (properties != NULL && properties->gateway_properties_entries != NULL) + { + /*Codes_SRS_GATEWAY_LL_14_009: [The function shall use each GATEWAY_PROPERTIES_ENTRY use each of GATEWAY_PROPERTIES's gateway_properties_entries to create and add a module to the GATEWAY_HANDLE message bus. ]*/ + size_t entries_count = VECTOR_size(properties->gateway_properties_entries); + if (entries_count > 0) + { + //Add the first module, if successfull add others + GATEWAY_PROPERTIES_ENTRY* entry = VECTOR_element(properties->gateway_properties_entries, 0); + MODULE_HANDLE module = gateway_addmodule_internal(gateway, entry->module_path, entry->module_configuration); + + //Continue adding modules until all are added or one fails + for (size_t properties_index = 1; properties_index < entries_count && module != NULL; ++properties_index) + { + entry = VECTOR_element(properties->gateway_properties_entries, properties_index); + module = gateway_addmodule_internal(gateway, entry->module_path, entry->module_configuration); + } + + /*Codes_SRS_GATEWAY_LL_14_036: [ If any MODULE_HANDLE is unable to be created from a GATEWAY_PROPERTIES_ENTRY the GATEWAY_HANDLE will be destroyed. ]*/ + if (module == NULL) + { + LogError("Gateway_LL_Create(): Unable to add module '%s'. The gateway will be destroyed.", entry->module_name); + while (gateway->modules != NULL && VECTOR_size(gateway->modules) > 0) + { + MODULE_DATA* module_data = (MODULE_DATA*)VECTOR_front(gateway->modules); + //By design, there will be no NULL module_data pointers in the vector + gateway_removemodule_internal(gateway, module_data); + } + VECTOR_destroy(gateway->modules); + MessageBus_Destroy(gateway->bus); + free(gateway); + gateway = NULL; + } + } + } + } + } + } + /*Codes_SRS_GATEWAY_LL_14_002: [This function shall return NULL upon any memory allocation failure.]*/ + else + { + LogError("Gateway_LL_Create(): malloc failed."); + } + + return gateway; +} + +void Gateway_LL_Destroy(GATEWAY_HANDLE gw) +{ + /*Codes_SRS_GATEWAY_LL_14_005: [If gw is NULL the function shall do nothing.]*/ + if (gw != NULL) + { + GATEWAY_HANDLE_DATA* gateway_handle = (GATEWAY_HANDLE_DATA*)gw; + + /*Codes_SRS_GATEWAY_LL_14_028: [The function shall remove each module in GATEWAY_HANDLE_DATA's modules vector and destroy GATEWAY_HANDLE_DATA's modules.]*/ + while (gateway_handle->modules != NULL && VECTOR_size(gateway_handle->modules) > 0) + { + MODULE_DATA* module_data = (MODULE_DATA*)VECTOR_front(gateway_handle->modules); + //By design, there will be no NULL module_data pointers in the vector + /*Codes_SRS_GATEWAY_LL_14_037: [If GATEWAY_HANDLE_DATA's message bus cannot unlink module, the function shall log the error and continue unloading the module from the GATEWAY_HANDLE. ]*/ + gateway_removemodule_internal(gateway_handle, module_data); + } + + VECTOR_destroy(gateway_handle->modules); + + /*Codes_SRS_GATEWAY_LL_14_006: [The function shall destroy the GATEWAY_HANDLE_DATA's `bus` `MESSAGE_BUS_HANDLE`. ]*/ + MessageBus_Destroy(gateway_handle->bus); + + free(gateway_handle); + } + else + { + LogError("Gateway_LL_Destroy(): The GATEWAY_HANDLE is null."); + } +} + +MODULE_HANDLE Gateway_LL_AddModule(GATEWAY_HANDLE gw, const GATEWAY_PROPERTIES_ENTRY* entry) +{ + MODULE_HANDLE module; + /*Codes_SRS_GATEWAY_LL_14_011: [ If gw, entry, or GATEWAY_PROPERTIES_ENTRY's module_path is NULL the function shall return NULL. ]*/ + if (gw != NULL && entry != NULL) + { + module = gateway_addmodule_internal(gw, entry->module_path, entry->module_configuration); + + if (module == NULL) + { + LogError("Gateway_LL_AddModule(): Unable to add module '%s'.", entry->module_name); + } + } + else + { + module = NULL; + LogError("Gateway_LL_AddModule(): Unable to add module to NULL GATEWAY_HANDLE or from NULL GATEWAY_PROPERTIES_ENTRY*. gw = %p, entry = %p.", gw, entry); + } + + return module; +} + +void Gateway_LL_RemoveModule(GATEWAY_HANDLE gw, MODULE_HANDLE module) +{ + /*Codes_SRS_GATEWAY_LL_14_020: [ If gw or module is NULL the function shall return. ]*/ + if (gw != NULL) + { + GATEWAY_HANDLE_DATA* gateway_handle = (GATEWAY_HANDLE_DATA*)gw; + + /*Codes_SRS_GATEWAY_LL_14_023: [The function shall locate the MODULE_DATA object in GATEWAY_HANDLE_DATA's modules containing module and return if it cannot be found. ]*/ + MODULE_DATA* module_data = VECTOR_find_if(gateway_handle->modules, module_data_find, module); + + if (module_data != NULL) + { + gateway_removemodule_internal(gateway_handle, module_data); + } + else + { + LogError("Gateway_LL_RemoveModule(): Failed to remove module because the MODULE_DATA pointer is NULL."); + } + } + else + { + LogError("Gateway_LL_RemoveModule(): Failed to remove module because the GATEWA_HANDLE is NULL."); + } +} + +/*Private*/ + +static MODULE_HANDLE gateway_addmodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, const char* module_path, const void* module_configuration) +{ + MODULE_HANDLE module_result; + if (module_path != NULL) + { + /*Codes_SRS_GATEWAY_LL_14_012: [The function shall load the module located at GATEWAY_PROPERTIES_ENTRY's module_path into a MODULE_LIBRARY_HANDLE. ]*/ + MODULE_LIBRARY_HANDLE module_library_handle = ModuleLoader_Load(module_path); + /*Codes_SRS_GATEWAY_LL_14_031: [If unsuccessful, the function shall return NULL.]*/ + if (module_library_handle == NULL) + { + module_result = NULL; + LogError("Failed to add module because the module located at [%s] could not be loaded.", module_path); + } + else + { + //Should always be a safe call. + /*Codes_SRS_GATEWAY_LL_14_013: [The function shall get the const MODULE_APIS* from the MODULE_LIBRARY_HANDLE.]*/ + const MODULE_APIS* module_apis = ModuleLoader_GetModuleAPIs(module_library_handle); + + /*Codes_SRS_GATEWAY_LL_14_015: [The function shall use the MODULE_APIS to create a MODULE_HANDLE using the GATEWAY_PROPERTIES_ENTRY's module_configuration. ]*/ + MODULE_HANDLE module = module_apis->Module_Create(gateway_handle->bus, module_configuration); + /*Codes_SRS_GATEWAY_LL_14_016: [If the module creation is unsuccessful, the function shall return NULL.]*/ + if (module == NULL) + { + module_result = NULL; + ModuleLoader_Unload(module_library_handle); + LogError("Module_Create failed."); + } + else + { + /*Codes_SRS_GATEWAY_LL_14_017: [The function shall link the module to the GATEWAY_HANDLE_DATA's bus using a call to MessageBus_AddModule. ]*/ + /*Codes_SRS_GATEWAY_LL_14_018: [If the message bus linking is unsuccessful, the function shall return NULL.]*/ + if (MessageBus_AddModule(gateway_handle->bus, module, module_apis) != MESSAGE_BUS_OK) + { + module_result = NULL; + LogError("Failed to add module to the gateway bus."); + } + else + { + /*Codes_SRS_GATEWAY_LL_14_039: [ The function shall increment the MESSAGE_BUS_HANDLE reference count if the MODULE_HANDLE was successfully linked to the GATEWAY_HANDLE_DATA's bus. ]*/ + MessageBus_IncRef(gateway_handle->bus); + /*Codes_SRS_GATEWAY_LL_14_029: [The function shall create a new MODULE_DATA containting the MODULE_HANDLE and MODULE_LIBRARY_HANDLE if the module was successfully linked to the message bus.]*/ + MODULE_DATA module_data = + { + module_library_handle, + module + }; + /*Codes_SRS_GATEWAY_LL_14_032: [The function shall add the new MODULE_DATA to GATEWAY_HANDLE_DATA's modules if the module was successfully linked to the message bus. ]*/ + if (VECTOR_push_back(gateway_handle->modules, &module_data, 1) != 0) + { + MessageBus_DecRef(gateway_handle->bus); + module_result = NULL; + if (MessageBus_RemoveModule(gateway_handle->bus, module) != MESSAGE_BUS_OK) + { + LogError("Failed to remove module [%p] from the gateway message bus. This module will remain linked.", module); + } + LogError("Unable to add MODULE_DATA* to the gateway module vector."); + } + else + { + /*Codes_SRS_GATEWAY_LL_14_019: [The function shall return the newly created MODULE_HANDLE only if each API call returns successfully.]*/ + module_result = module; + } + } + + /*Codes_SRS_GATEWAY_LL_14_030: [If any internal API call is unsuccessful after a module is created, the library will be unloaded and the module destroyed.]*/ + if (module_result == NULL) + { + module_apis->Module_Destroy(module); + ModuleLoader_Unload(module_library_handle); + } + } + } + } + /*Codes_SRS_GATEWAY_LL_14_011: [If gw, entry, or GATEWAY_PROPERTIES_ENTRY's module_path is NULL the function shall return NULL. ]*/ + else + { + module_result = NULL; + LogError("Failed to add module because either the GATEWAY_HANDLE is NULL or the module_path string is NULL or empty. gw = %p, module_path = '%s'.", gateway_handle, module_path); + } + return module_result; +} + +static void gateway_removemodule_internal(GATEWAY_HANDLE_DATA* gateway_handle, MODULE_DATA* module_data) +{ + /*Codes_SRS_GATEWAY_LL_14_021: [ The function shall unlink module from the GATEWAY_HANDLE_DATA's bus MESSAGE_BUS_HANDLE. ]*/ + /*Codes_SRS_GATEWAY_LL_14_022: [ If GATEWAY_HANDLE_DATA's bus cannot unlink module, the function shall log the error and continue unloading the module from the GATEWAY_HANDLE. ]*/ + if (MessageBus_RemoveModule(gateway_handle->bus, module_data->module) != MESSAGE_BUS_OK) + { + LogError("Failed to remove module [%p] from the message bus. This module will remain linked to the message bus but will be removed from the gateway.", module_data->module); + } + /*Codes_SRS_GATEWAY_LL_14_038: [ The function shall decrement the MESSAGE_BUS_HANDLE reference count. ]*/ + MessageBus_DecRef(gateway_handle->bus); + /*Codes_SRS_GATEWAY_LL_14_024: [ The function shall use the MODULE_DATA's module_library_handle to retrieve the MODULE_APIS and destroy module. ]*/ + ModuleLoader_GetModuleAPIs(module_data->module_library_handle)->Module_Destroy(module_data->module); + /*Codes_SRS_GATEWAY_LL_14_025: [The function shall unload MODULE_DATA's module_library_handle. ]*/ + ModuleLoader_Unload(module_data->module_library_handle); + /*Codes_SRS_GATEWAY_LL_14_026:[The function shall remove that MODULE_DATA from GATEWAY_HANDLE_DATA's modules. ]*/ + VECTOR_erase(gateway_handle->modules, module_data, 1); +} + +static bool module_data_find(const void* element, const void* value) +{ + return ((MODULE_DATA*)element)->module == value; +} \ No newline at end of file diff --git a/core/src/message.c b/core/src/message.c new file mode 100644 index 00000000..0b9cef1d --- /dev/null +++ b/core/src/message.c @@ -0,0 +1,244 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include "azure_c_shared_utility/gballoc.h" + +#include + +#include "message.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/constmap.h" +#include "azure_c_shared_utility/iot_logging.h" + +#include "azure_c_shared_utility/refcount.h" + +typedef struct MESSAGE_HANDLE_DATA_TAG +{ + CONSTMAP_HANDLE properties; + CONSTBUFFER_HANDLE content; +}MESSAGE_HANDLE_DATA; + +DEFINE_REFCOUNT_TYPE(MESSAGE_HANDLE_DATA); + +MESSAGE_HANDLE Message_Create(const MESSAGE_CONFIG * cfg) +{ + MESSAGE_HANDLE_DATA* result; + /*Codes_SRS_MESSAGE_02_002: [If cfg is NULL then Message_Create shall return NULL.]*/ + if (cfg == NULL) + { + result = NULL; + LogError("invalid parameter (NULL)."); + } + /*Codes_SRS_MESSAGE_02_003: [If field source of cfg is NULL and size is not zero, then Message_Create shall fail and return NULL.] */ + else if ((cfg->size > 0) && (cfg->source == NULL)) + { + result = NULL; + LogError("invalid parameter combination cfg->size=%zd, cfg->source=%p", cfg->size, cfg->source); + } + else + { + /*Codes_SRS_MESSAGE_02_006: [Otherwise, Message_Create shall return a non-NULL handle and shall set the internal ref count to "1".]*/ + result = REFCOUNT_TYPE_CREATE(MESSAGE_HANDLE_DATA); + if (result == NULL) + { + LogError("malloc returned NULL"); + /*return as is*/ + } + else + { + /*Codes_SRS_MESSAGE_02_004: [Mesages shall be allowed to be created from zero-size content.]*/ + /*Codes_SRS_MESSAGE_02_015: [The MESSAGE_CONTENT's field size shall have the same value as the cfg's field size.]*/ + /*Codes_SRS_MESSAGE_17_003: [Message_Create shall copy the source to a readonly CONSTBUFFER.]*/ + result->content = CONSTBUFFER_Create(cfg->source, cfg->size); + if (result->content == NULL) + { + LogError("CONSBUFFER Create failed"); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_MESSAGE_02_019: [Message_Create shall clone the sourceProperties to a readonly CONSTMAP.]*/ + result->properties = ConstMap_Create(cfg->sourceProperties); + if (result->properties == NULL) + { + /*Codes_SRS_MESSAGE_02_005: [If Message_Create encounters an error while building the internal structures of the message, then it shall return NULL.] */ + LogError("ConstMap_Create failed"); + CONSTBUFFER_Destroy(result->content); + free(result); + result = NULL; + } + else + { + /*all is fine, return as is.*/ + } + } + } + } + return (MESSAGE_HANDLE)result; +} + +MESSAGE_HANDLE Message_CreateFromBuffer(const MESSAGE_BUFFER_CONFIG* cfg) +{ + MESSAGE_HANDLE_DATA* result; + /*Codes_SRS_MESSAGE_17_008: [ If cfg is NULL then Message_CreateFromBuffer shall return NULL.] */ + if (cfg == NULL) + { + result = NULL; + LogError("invalid parameter (NULL)."); + } + /* Codes_SRS_MESSAGE_17_009: [If field sourceContent of cfg is NULL, then Message_CreateFromBuffer shall fail and return NULL.]*/ + else if (cfg->sourceContent == NULL) + { + result = NULL; + LogError("invalid CONSTBUFFER (NULL)"); + } + /*Codes_SRS_MESSAGE_17_010: [If field sourceProperties of cfg is NULL, then Message_CreateFromBuffer shall fail and return NULL.]*/ + else if (cfg->sourceProperties == NULL) + { + result = NULL; + LogError("invalid properties (NULL)"); + } + else + { + /*Codes_SRS_MESSAGE_17_011: [If Message_CreateFromBuffer encounters an error while building the internal structures of the message, then it shall return NULL.]*/ + /*Codes_SRS_MESSAGE_17_014: [On success, Message_CreateFromBuffer shall return a non-NULL handle and set the internal ref count to "1".]*/ + result = REFCOUNT_TYPE_CREATE(MESSAGE_HANDLE_DATA); + if (result == NULL) + { + LogError("malloc returned NULL"); + /*return as is*/ + } + else + { + /*Codes_SRS_MESSAGE_17_013: [Message_CreateFromBuffer shall clone the CONSTBUFFER sourceBuffer.]*/ + result->content = CONSTBUFFER_Clone(cfg->sourceContent); + if (result->content == NULL) + { + LogError("CONSBUFFER Clone failed"); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_MESSAGE_17_012: [Message_CreateFromBuffer shall copy the sourceProperties to a readonly CONSTMAP.]*/ + result->properties = ConstMap_Create(cfg->sourceProperties); + if (result->properties == NULL) + { + LogError("ConstMap_Create failed"); + CONSTBUFFER_Destroy(result->content); + free(result); + result = NULL; + } + else + { + /*all is fine, return as is.*/ + } + } + } + } + return (MESSAGE_HANDLE)result; +} + +MESSAGE_HANDLE Message_Clone(MESSAGE_HANDLE message) +{ + + if (message == NULL) + { + LogError("invalid arg: message is NULL"); + /*Codes_SRS_MESSAGE_02_007: [If messageHandle is NULL then Message_Clone shall return NULL.] */ + } + else + { + /*Codes_SRS_MESSAGE_02_008: [Otherwise, Message_Clone shall increment the internal ref count.] */ + INC_REF(MESSAGE_HANDLE_DATA, message); + MESSAGE_HANDLE_DATA* messageData = (MESSAGE_HANDLE_DATA*)message; + /*Codes_SRS_MESSAGE_17_001: [Message_Clone shall clone the CONSTMAP handle.]*/ + (void)ConstMap_Clone(messageData->properties); + /*Codes_SRS_MESSAGE_17_004: [Message_Clone shall clone the CONSTBUFFER handle]*/ + (void)CONSTBUFFER_Clone(messageData->content); + } + /*Codes_SRS_MESSAGE_02_010: [Message_Clone shall return messageHandle.]*/ + return message; +} + +CONSTMAP_HANDLE Message_GetProperties(MESSAGE_HANDLE message) +{ + CONSTMAP_HANDLE result; + /*Codes_SRS_MESSAGE_02_011: [If message is NULL then Message_GetProperties shall return NULL.] */ + if (message == NULL) + { + LogError("invalid arg: message is NULL"); + result = NULL; + } + else + { + /*Codes_SRS_MESSAGE_02_012: [Otherwise, Message_GetProperties shall shall clone and return the CONSTMAP handle representing the properties of the message.]*/ + MESSAGE_HANDLE_DATA* messageData = (MESSAGE_HANDLE_DATA*)message; + result = ConstMap_Clone(messageData->properties); + } + return result; +} + +const CONSTBUFFER * Message_GetContent(MESSAGE_HANDLE message) +{ + const CONSTBUFFER* result; + /*Codes_SRS_MESSAGE_02_013: [If message is NULL then Message_GetContent shall return NULL.] */ + if (message == NULL) + { + LogError("invalid argument, message is NULL"); + result = NULL; + } + else + { + /*Codes_SRS_MESSAGE_02_014: [Otherwise, Message_GetContent shall return a non-NULL const pointer to a structure of type MESSAGE_CONTENT.]*/ + /*Codes_SRS_MESSAGE_02_016: [The CONSTBUFFER's field buffer shall compare equal byte-by-byte to the cfg's field source.]*/ + result = CONSTBUFFER_GetContent(((MESSAGE_HANDLE_DATA*)message)->content); + } + return result; +} + +CONSTBUFFER_HANDLE Message_GetContentHandle(MESSAGE_HANDLE message) +{ + CONSTBUFFER_HANDLE result; + if (message == NULL) + { + /*Codes_SRS_MESSAGE_17_006: [If message is NULL then Message_GetContentHandle shall return NULL.] */ + LogError("invalid argument, message is NULL"); + result = NULL; + } + else + { + /*Codes_SRS_MESSAGE_17_007: [Otherwise, Message_GetContentHandle shall shall clone and return the CONSTBUFFER_HANDLE representing the message content.]*/ + result = CONSTBUFFER_Clone(((MESSAGE_HANDLE_DATA*)message)->content); + } + return result; +} + +void Message_Destroy(MESSAGE_HANDLE message) +{ + /*Codes_SRS_MESSAGE_02_017: [If message is NULL then Message_Destroy shall do nothing.] */ + if (message == NULL) + { + LogError("invalid arg: message is NULL"); + } + else + { + MESSAGE_HANDLE_DATA* messageData = (MESSAGE_HANDLE_DATA*)message; + /*Codes_SRS_MESSAGE_17_002: [Message_Destroy shall destroy the CONSTMAP properties.]*/ + ConstMap_Destroy(messageData->properties); + /*Codes_SRS_MESSAGE_17_005: [Message_Destroy shall destroy the CONSTBUFFER.]*/ + CONSTBUFFER_Destroy(messageData->content); + /*Codes_SRS_MESSAGE_02_020: [Otherwise, Message_Destroy shall decrement the internal ref count of the message.]*/ + if (DEC_REF(MESSAGE_HANDLE_DATA, message) == DEC_RETURN_ZERO) + { + /*Codes_SRS_MESSAGE_02_021: [If the ref count is zero then the allocated resources are freed.]*/ + free(message); + } + } +} diff --git a/core/src/message_bus.c b/core/src/message_bus.c new file mode 100644 index 00000000..9a3f3c8d --- /dev/null +++ b/core/src/message_bus.c @@ -0,0 +1,615 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include +#include + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/condition.h" +#include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/refcount.h" +#include "azure_c_shared_utility/list.h" + +#include "message.h" +#include "module.h" +#include "message_bus.h" + +/*The message bus implementation shall use the following definition as the backing structure for the message bus handle*/ +typedef struct MESSAGE_BUS_HANDLE_DATA_TAG +{ + LIST_HANDLE modules; + LOCK_HANDLE modules_lock; +}MESSAGE_BUS_HANDLE_DATA; + +DEFINE_REFCOUNT_TYPE(MESSAGE_BUS_HANDLE_DATA); + +typedef struct MESSAGE_BUS_MODULEINFO_TAG +{ + /** + * Handle to the module that's connected to the bus. + */ + MODULE_HANDLE module; + + /** + * The function dispatch table for this module. + */ + const MODULE_APIS* module_apis; + + /** + * Handle to the thread on which this module's message processing loop is + * running. + */ + THREAD_HANDLE thread; + + /** + * Handle to the queue of messages to be delivered to this module. + */ + VECTOR_HANDLE mq; + + /** + * Lock used to synchronize access to the 'mq' field. + */ + LOCK_HANDLE mq_lock; + + /** + * A condition variable that is signaled when there are new messages. + */ + COND_HANDLE mq_cond; + + /** + * Message publish worker will keep running while this is false. + */ + volatile sig_atomic_t quit_worker; +}MESSAGE_BUS_MODULEINFO; + +// This variable is used only for unit testing purposes. +size_t BUS_offsetof_quit_worker = offsetof(MESSAGE_BUS_MODULEINFO, quit_worker); + +MESSAGE_BUS_HANDLE MessageBus_Create(void) +{ + MESSAGE_BUS_HANDLE_DATA* result; + + /*Codes_SRS_MESSAGE_BUS_13_067: [MessageBus_Create shall malloc a new instance of MESSAGE_BUS_HANDLE_DATA and return NULL if it fails.]*/ + result = REFCOUNT_TYPE_CREATE(MESSAGE_BUS_HANDLE_DATA); + if (result == NULL) + { + LogError("malloc returned NULL"); + /*return as is*/ + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_007: [MessageBus_Create shall initialize MESSAGE_BUS_HANDLE_DATA::modules with a valid VECTOR_HANDLE.]*/ + result->modules = list_create(); + if (result->modules == NULL) + { + /*Codes_SRS_MESSAGE_BUS_13_003: [This function shall return NULL if an underlying API call to the platform causes an error.]*/ + LogError("VECTOR_create failed"); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_023: [MessageBus_Create shall initialize MESSAGE_BUS_HANDLE_DATA::modules_lock with a valid LOCK_HANDLE.]*/ + result->modules_lock = Lock_Init(); + if (result->modules_lock == NULL) + { + /*Codes_SRS_MESSAGE_BUS_13_003: [This function shall return NULL if an underlying API call to the platform causes an error.]*/ + LogError("Lock_Init failed"); + list_destroy(result->modules); + free(result); + result = NULL; + } + } + } + + /*Codes_SRS_MESSAGE_BUS_13_001: [This API shall yield a MESSAGE_BUS_HANDLE representing the newly created message bus.This handle value shall not be equal to NULL when the API call is successful.]*/ + return result; +} + +void MessageBus_IncRef(MESSAGE_BUS_HANDLE bus) +{ + /*Codes_SRS_MESSAGE_BUS_13_108: [If `bus` is NULL then MessageBus_IncRef shall do nothing.]*/ + if (bus == NULL) + { + LogError("invalid arg: bus is NULL"); + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_109: [Otherwise, MessageBus_IncRef shall increment the internal ref count.]*/ + INC_REF(MESSAGE_BUS_HANDLE_DATA, bus); + } +} + +/** +* This is the worker function that runs for each module. The module_publish_worker +* function is passed in a pointer to the relevant MODULE_INFO object as it's +* thread context parameter. The function's job is to basically wait on the +* mq_cond condition variable and process messages in module.mq when the +* condition is signaled. +*/ +static int module_publish_worker(void * user_data) +{ + /*Codes_SRS_MESSAGE_BUS_13_026: [This function shall assign `user_data` to a local variable called `module_info` of type `MESSAGE_BUS_MODULEINFO*`.]*/ + MESSAGE_BUS_MODULEINFO* module_info = (MESSAGE_BUS_MODULEINFO*)user_data; + + /*Codes_SRS_MESSAGE_BUS_13_089: [This function shall acquire the lock on module_info->mq_lock.]*/ + if (Lock(module_info->mq_lock) != LOCK_OK) + { + /*Codes_SRS_MESSAGE_BUS_02_004: [ If acquiring the lock fails, then module_publish_worker shall return. ]*/ + LogError("unable to lock"); + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_068: [This function shall run a loop that keeps running while module_info->quit_worker is equal to 0.]*/ + while (module_info->quit_worker == 0) + { + /*Codes_SRS_MESSAGE_BUS_13_071: [For every iteration of the loop the function will first wait on module_info->mq_cond using module_info->mq_lock as the corresponding mutex to be used by the condition variable.]*/ + /*Codes_SRS_MESSAGE_BUS_04_001: [This function shall immediately start processing messages when `module->mq` is not empty without waiting on `module->mq_cond`.] */ + + /*this condition accounts for the case where the message has been enqueued in the past, and the condition has been signalled in the past, and this thread */ + /*is still at static int module_publish_worker(void * user_data), that is, didn't get to execute Lock(...)*/ + if ((VECTOR_size(module_info->mq) > 0) || (Condition_Wait(module_info->mq_cond, module_info->mq_lock, 0) == COND_OK)) + { + /*Codes_SRS_MESSAGE_BUS_13_090: [When module_info->mq_cond has been signaled this function shall kick off another loop predicated on module_info->quit_worker being equal to 0 and module_info->mq not being empty.This thread has the lock on module_info->mq_lock at this point.]*/ + LOCK_RESULT lock_result = LOCK_OK; + while ((module_info->quit_worker == 0) && VECTOR_size(module_info->mq) > 0) + { + /*Codes_SRS_MESSAGE_BUS_13_069: [The function shall dequeue a message from the module's message queue. ]*/ + MESSAGE_HANDLE *pmsg = (MESSAGE_HANDLE*)VECTOR_front(module_info->mq); + MESSAGE_HANDLE msg = *pmsg; + VECTOR_erase(module_info->mq, pmsg, 1); + + /*Codes_SRS_MESSAGE_BUS_13_091: [The function shall unlock module_info->mq_lock.]*/ + if (Unlock(module_info->mq_lock) != LOCK_OK) + { + LogError("unable to unlock"); + + /*Codes_SRS_MESSAGE_BUS_13_093: [The function shall destroy the message that was dequeued by calling Message_Destroy.]*/ + Message_Destroy(msg); + + continue; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_092: [The function shall deliver the message to the module's callback function via module_info->module_apis. ]*/ + module_info->module_apis->Module_Receive(module_info->module, msg); + + /*Codes_SRS_MESSAGE_BUS_13_093: [The function shall destroy the message that was dequeued by calling Message_Destroy.]*/ + Message_Destroy(msg); + + /*Codes_SRS_MESSAGE_BUS_13_094: [The function shall re - acquire the lock on module_info->mq_lock.]*/ + if ((lock_result = Lock(module_info->mq_lock)) != LOCK_OK) + { + LogError("unable to lock"); + break; + } + } + } + } + else + { + LogError("Lock/Condition_Wait has failed. Bailing."); + break; + } + } + + /*Codes_SRS_MESSAGE_BUS_13_095: [When the function exits the outer loop predicated on module_info->quit_worker being 0 it shall unlock module_info->mq_lock before exiting from the function.]*/ + if (Unlock(module_info->mq_lock) != LOCK_OK) + { + LogError("unable to unlock - module worker was terminating"); + } + } + + + + return 0; +} + +static MESSAGE_BUS_RESULT init_module(MESSAGE_BUS_MODULEINFO* module_info, MODULE_HANDLE module, const MODULE_APIS* module_apis) +{ + MESSAGE_BUS_RESULT result; + + /*Codes_SRS_MESSAGE_BUS_13_107: The function shall assign the `module` handle to `MESSAGE_BUS_MODULEINFO::module`.*/ + module_info->module = module; + + /*Codes_SRS_MESSAGE_BUS_13_097: [The function shall assign module_apis to MESSAGE_BUS_MODULEINFO::module_apis.]*/ + module_info->module_apis = module_apis; + + /*Codes_SRS_MESSAGE_BUS_13_098: [The function shall initialize MESSAGE_BUS_MODULEINFO::mq with a valid vector handle.]*/ + module_info->mq = VECTOR_create(sizeof(MESSAGE_HANDLE)); + if (module_info->mq == NULL) + { + LogError("VECTOR_create failed"); + result = MESSAGE_BUS_ERROR; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_099: [The function shall initialize MESSAGE_BUS_MODULEINFO::mq_lock with a valid lock handle.]*/ + module_info->mq_lock = Lock_Init(); + if (module_info->mq_lock == NULL) + { + LogError("Lock_Init failed"); + VECTOR_destroy(module_info->mq); + result = MESSAGE_BUS_ERROR; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_100: [The function shall initialize MESSAGE_BUS_MODULEINFO::mq_cond with a valid condition handle.]*/ + module_info->mq_cond = Condition_Init(); + if (module_info->mq_cond == NULL) + { + LogError("Condition_Init failed"); + Lock_Deinit(module_info->mq_lock); + VECTOR_destroy(module_info->mq); + result = MESSAGE_BUS_ERROR; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_101: [The function shall assign 0 to MESSAGE_BUS_MODULEINFO::quit_worker.]*/ + module_info->quit_worker = 0; + result = MESSAGE_BUS_OK; + } + } + } + + return result; +} + +static void deinit_module(MESSAGE_BUS_MODULEINFO* module_info) +{ + /*Codes_SRS_MESSAGE_BUS_13_057: [The function shall free all members of the MODULE_INFO object.]*/ + VECTOR_destroy(module_info->mq); + Condition_Deinit(module_info->mq_cond); + Lock_Deinit(module_info->mq_lock); +} + +static MESSAGE_BUS_RESULT start_module(MESSAGE_BUS_MODULEINFO* module_info) +{ + MESSAGE_BUS_RESULT result; + + /*Codes_SRS_MESSAGE_BUS_13_102: [The function shall create a new thread for the module by calling ThreadAPI_Create using module_publish_worker as the thread callback and using the newly allocated MESSAGE_BUS_MODULEINFO object as the thread context.*/ + if (ThreadAPI_Create( + &(module_info->thread), + module_publish_worker, + (void*)module_info + ) != THREADAPI_OK) + { + LogError("ThreadAPI_Create failed"); + result = MESSAGE_BUS_ERROR; + } + else + { + result = MESSAGE_BUS_OK; + } + + return result; +} + +/*stop module means: stop the thread that feeds messages to Module_Receive function + deletion of all queued messages */ +/*returns 0 if success, otherwise __LINE__*/ +static int stop_module(MESSAGE_BUS_MODULEINFO* module_info) +{ + int thread_result, result; + size_t len, i; + /*Codes_SRS_MESSAGE_BUS_02_001: [ MessageBus_RemoveModule shall lock `MESSAGE_BUS_MODULEINFO::mq_lock`. ]*/ + if (Lock(module_info->mq_lock) != LOCK_OK) + { + module_info->quit_worker = 1; /*at the cost of a data race, still try to stop the module*/ + /*Codes_SRS_MESSAGE_BUS_02_002: [ If locking fails, then terminating the thread shall not be attempted (signalling the condition and joining the thread). ]*/ + LogError("unable to lock mq_lock"); + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_103: [The function shall assign 1 to MESSAGE_BUS_MODULEINFO::quit_worker.]*/ + module_info->quit_worker = 1; + + /*Codes_SRS_MESSAGE_BUS_17_001: [The function shall signal MESSAGE_BUS_MODULEINFO::mq_cond to release module from waiting.]*/ + if (Condition_Post(module_info->mq_cond) != COND_OK) + { + LogError("Condition_Post failed for module at item [%p] failed", module_info); + } + else + { + /*all is fine, thread will eventually stop and be joined*/ + } + + /*Codes_SRS_MESSAGE_BUS_02_003: [ After signaling the condition, MessageBus_RemoveModule shall unlock MESSAGE_BUS_MODULEINFO::mq_lock. ]*/ + if (Unlock(module_info->mq_lock) != LOCK_OK) + { + LogError("unable to unlock mq_lock"); + } + } + + /*Codes_SRS_MESSAGE_BUS_13_104: [The function shall wait for the module's thread to exit by joining MESSAGE_BUS_MODULEINFO::thread via ThreadAPI_Join. ]*/ + if (ThreadAPI_Join(module_info->thread, &thread_result) != THREADAPI_OK) + { + result = __LINE__; + LogError("ThreadAPI_Join() returned an error."); + } + else + { + result = 0; + } + + /*Codes_SRS_MESSAGE_BUS_13_056: [If MESSAGE_BUS_MODULEINFO::mq is not empty then this function shall call Message_Destroy on every message still left in the collection.]*/ + len = VECTOR_size(module_info->mq); + for (i = 0; i < len; i++) + { + // this MUST NOT be NULL + MESSAGE_HANDLE* msg = (MESSAGE_HANDLE*)VECTOR_element(module_info->mq, i); + Message_Destroy(*msg); + } + return result; +} + +MESSAGE_BUS_RESULT MessageBus_AddModule(MESSAGE_BUS_HANDLE bus, MODULE_HANDLE module, const MODULE_APIS* module_apis) +{ + MESSAGE_BUS_RESULT result; + + /*Codes_SRS_MESSAGE_BUS_13_038: [If `bus` or `module` or `module_apis` is NULL the function shall return MESSAGE_BUS_INVALIDARG.]*/ + if (bus == NULL || module == NULL || module_apis == NULL) + { + result = MESSAGE_BUS_INVALIDARG; + LogError("invalid parameter (NULL)."); + } + else + { + MESSAGE_BUS_MODULEINFO* module_info = (MESSAGE_BUS_MODULEINFO*)malloc(sizeof(MESSAGE_BUS_MODULEINFO)); + if (module_info == NULL) + { + LogError("Allocate module info failed"); + result = MESSAGE_BUS_ERROR; + } + else + { + if (init_module(module_info, module, module_apis) != MESSAGE_BUS_OK) + { + /*Codes_SRS_MESSAGE_BUS_13_047: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.]*/ + LogError("start_module failed"); + free(module_info); + result = MESSAGE_BUS_ERROR; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_039: [This function shall acquire the lock on MESSAGE_BUS_HANDLE_DATA::modules_lock.]*/ + MESSAGE_BUS_HANDLE_DATA* bus_data = (MESSAGE_BUS_HANDLE_DATA*)bus; + if (Lock(bus_data->modules_lock) != LOCK_OK) + { + /*Codes_SRS_MESSAGE_BUS_13_047: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.]*/ + LogError("Lock on bus_data->modules_lock failed"); + deinit_module(module_info); + free(module_info); + result = MESSAGE_BUS_ERROR; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_045: [MessageBus_AddModule shall append the new instance of MESSAGE_BUS_MODULEINFO to MESSAGE_BUS_HANDLE_DATA::modules.]*/ + LIST_ITEM_HANDLE moduleListItem = list_add(bus_data->modules, module_info); + if (moduleListItem == NULL) + { + /*Codes_SRS_MESSAGE_BUS_13_047: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.]*/ + LogError("list_add failed"); + deinit_module(module_info); + free(module_info); + result = MESSAGE_BUS_ERROR; + } + else + { + if (start_module(module_info) != MESSAGE_BUS_OK) + { + LogError("start_module failed"); + deinit_module(module_info); + list_remove(bus_data->modules, moduleListItem); + free(module_info); + result = MESSAGE_BUS_ERROR; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_047: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.]*/ + result = MESSAGE_BUS_OK; + } + } + + /*Codes_SRS_MESSAGE_BUS_13_046: [This function shall release the lock on MESSAGE_BUS_HANDLE_DATA::modules_lock.]*/ + Unlock(bus_data->modules_lock); + } + } + + } + } + + return result; +} + +static bool find_module_predicate(LIST_ITEM_HANDLE list_item, const void* value) +{ + MESSAGE_BUS_MODULEINFO* element = (MESSAGE_BUS_MODULEINFO*)list_item_get_value(list_item); + return (element->module == value); +} + +MESSAGE_BUS_RESULT MessageBus_RemoveModule(MESSAGE_BUS_HANDLE bus, MODULE_HANDLE module) +{ + /*Codes_SRS_MESSAGE_BUS_13_048: [If `bus` or `module` is NULL the function shall return MESSAGE_BUS_INVALIDARG.]*/ + MESSAGE_BUS_RESULT result; + if (bus == NULL || module == NULL) + { + result = MESSAGE_BUS_INVALIDARG; + LogError("invalid parameter (NULL)."); + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_088: [This function shall acquire the lock on MESSAGE_BUS_HANDLE_DATA::modules_lock.]*/ + MESSAGE_BUS_HANDLE_DATA* bus_data = (MESSAGE_BUS_HANDLE_DATA*)bus; + if (Lock(bus_data->modules_lock) != LOCK_OK) + { + /*Codes_SRS_MESSAGE_BUS_13_053: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.]*/ + LogError("Lock on bus_data->modules_lock failed"); + result = MESSAGE_BUS_ERROR; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_049: [MessageBus_RemoveModule shall perform a linear search for module in MESSAGE_BUS_HANDLE_DATA::modules.]*/ + LIST_ITEM_HANDLE module_info_item = list_find(bus_data->modules, find_module_predicate, module); + + if (module_info_item == NULL) + { + /*Codes_SRS_MESSAGE_BUS_13_050: [MessageBus_RemoveModule shall unlock MESSAGE_BUS_HANDLE_DATA::modules_lock and return MESSAGE_BUS_ERROR if the module is not found in MESSAGE_BUS_HANDLE_DATA::modules.]*/ + LogError("Supplied module was not found on the bus"); + result = MESSAGE_BUS_ERROR; + } + else + { + MESSAGE_BUS_MODULEINFO* module_info = (MESSAGE_BUS_MODULEINFO*)list_item_get_value(module_info_item); + if (stop_module(module_info) == 0) + { + deinit_module(module_info); + } + else + { + LogError("unable to stop module"); + } + + /*Codes_SRS_MESSAGE_BUS_13_052: [The function shall remove the module from MESSAGE_BUS_HANDLE_DATA::modules.]*/ + list_remove(bus_data->modules, module_info_item); + free(module_info); + + /*Codes_SRS_MESSAGE_BUS_13_053: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.]*/ + result = MESSAGE_BUS_OK; + } + + /*Codes_SRS_MESSAGE_BUS_13_054: [This function shall release the lock on MESSAGE_BUS_HANDLE_DATA::modules_lock.]*/ + Unlock(bus_data->modules_lock); + } + } + + return result; +} + +static void bus_decrement_ref(MESSAGE_BUS_HANDLE bus) +{ + /*Codes_SRS_MESSAGE_BUS_13_058: [If `bus` is NULL the function shall do nothing.]*/ + if (bus != NULL) + { + /*Codes_SRS_MESSAGE_BUS_13_111: [Otherwise, MessageBus_Destroy shall decrement the internal ref count of the message.]*/ + /*Codes_SRS_MESSAGE_BUS_13_112: [If the ref count is zero then the allocated resources are freed.]*/ + if (DEC_REF(MESSAGE_BUS_HANDLE_DATA, bus) == DEC_RETURN_ZERO) + { + MESSAGE_BUS_HANDLE_DATA* bus_data = (MESSAGE_BUS_HANDLE_DATA*)bus; + if (list_get_head_item(bus_data->modules) != NULL) + { + LogError("WARNING: There are still active modules connected to the bus and the bus is being destroyed."); + } + + list_destroy(bus_data->modules); + Lock_Deinit(bus_data->modules_lock); + free(bus_data); + } + } + else + { + LogError("bus handle is NULL"); + } +} + +extern void MessageBus_Destroy(MESSAGE_BUS_HANDLE bus) +{ + bus_decrement_ref(bus); +} + +extern void MessageBus_DecRef(MESSAGE_BUS_HANDLE bus) +{ + /*Codes_SRS_MESSAGE_BUS_13_113: [This function shall implement all the requirements of the MessageBus_Destroy API.]*/ + bus_decrement_ref(bus); +} + +MESSAGE_BUS_RESULT MessageBus_Publish(MESSAGE_BUS_HANDLE bus, MESSAGE_HANDLE message) +{ + MESSAGE_BUS_RESULT result; + /*Codes_SRS_MESSAGE_BUS_13_030: [If bus or message is NULL the function shall return MESSAGE_BUS_INVALIDARG.]*/ + if (bus == NULL || message == NULL) + { + result = MESSAGE_BUS_INVALIDARG; + LogError("Bus handle and/or message handle is NULL"); + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_031: [MessageBus_Publish shall acquire the lock MESSAGE_BUS_HANDLE_DATA::modules_lock.]*/ + MESSAGE_BUS_HANDLE_DATA* bus_data = (MESSAGE_BUS_HANDLE_DATA*)bus; + if (Lock(bus_data->modules_lock) != LOCK_OK) + { + LogError("Lock on bus_data->modules_lock failed"); + result = MESSAGE_BUS_ERROR; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_032: [MessageBus_Publish shall start a processing loop for every module in MESSAGE_BUS_HANDLE_DATA::modules.]*/ + + /*Codes_SRS_MESSAGE_BUS_13_037: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.]*/ + result = MESSAGE_BUS_OK; + + // NOTE: This is a best-effort delivery bus which means that we offer no + // delivery guarantees. If message delivery for a particular module fails, + // we log the fact and go on our merry way trying to deliver messages to + // other modules on the bus. We will however return MESSAGE_BUS_ERROR when this + // happens. + for (LIST_ITEM_HANDLE current_module = list_get_head_item(bus_data->modules); + current_module != NULL; + current_module = list_get_next_item(current_module)) + { + + MESSAGE_BUS_MODULEINFO* module_info = (MESSAGE_BUS_MODULEINFO*)list_item_get_value(current_module); + + /*Codes_SRS_MESSAGE_BUS_13_033: [In the loop, the function shall first acquire the lock on MESSAGE_BUS_MODULEINFO::mq_lock.]*/ + if (Lock(module_info->mq_lock) != LOCK_OK) + { + /*Codes_SRS_MESSAGE_BUS_13_037: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.]*/ + LogError("Lock on module_info->mq_lock for module at item [%p] failed", current_module); + result = MESSAGE_BUS_ERROR; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_034: [The function shall then append message to MESSAGE_BUS_MODULEINFO::mq by calling Message_Clone and VECTOR_push_back.]*/ + MESSAGE_HANDLE msg = Message_Clone(message); + if (VECTOR_push_back(module_info->mq, &msg, 1) != 0) + { + /*Codes_SRS_MESSAGE_BUS_13_037: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.]*/ + LogError("VECTOR_push_back failed for module at item [%p] failed", current_module); + Message_Destroy(msg); + Unlock(module_info->mq_lock); + result = MESSAGE_BUS_ERROR; + } + else + { + /*Codes_SRS_MESSAGE_BUS_13_096: [The function shall then signal MESSAGE_BUS_MODULEINFO::mq_cond.]*/ + if (Condition_Post(module_info->mq_cond) != COND_OK) + { + /*Codes_SRS_MESSAGE_BUS_13_037: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.]*/ + LogError("Condition_Post failed for module at item [%p] failed", current_module); + result = MESSAGE_BUS_ERROR; + } + + /*Codes_SRS_MESSAGE_BUS_13_035: [The function shall then release MESSAGE_BUS_MODULEINFO::mq_lock.]*/ + if(Unlock(module_info->mq_lock)!=LOCK_OK) + { + LogError("unable to unlock"); + } + } + } + } + + /*Codes_SRS_MESSAGE_BUS_13_040: [MessageBus_Publish shall release the lock MESSAGE_BUS_HANDLE_DATA::modules_lock after the loop.]*/ + Unlock(bus_data->modules_lock); + } + } + + return result; +} \ No newline at end of file diff --git a/core/src/module_loader.c b/core/src/module_loader.c new file mode 100644 index 00000000..5dd7b357 --- /dev/null +++ b/core/src/module_loader.c @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include "azure_c_shared_utility/gballoc.h" +#include + +#include "azure_c_shared_utility/iot_logging.h" + +#include "module_loader.h" +#include "dynamic_library.h" + +typedef struct MODULE_LIBRARY_HANDLE_DATA_TAG +{ + void* library; + const MODULE_APIS* apis; +}MODULE_LIBRARY_HANDLE_DATA; + + + +MODULE_LIBRARY_HANDLE ModuleLoader_Load(const char* moduleLibraryFileName) +{ + MODULE_LIBRARY_HANDLE_DATA* result; + + // moduleLibraryFileName cannot be null/empty + if (moduleLibraryFileName == NULL) + { + /*Codes_SRS_MODULE_LOADER_17_001: [ModuleLoader_Load shall validate the moduleLibraryFileName, if it is NULL, it will return NULL.]*/ + result = NULL; + LogError("ModuleLoader_Load() - moduleLibraryFileName is NULL"); + } + else + { + /* Codes_SRS_MODULE_LOADER_17_005: [ModuleLoader_Load shall allocate memory for the structure MODULE_LIBRARY_HANDLE.] */ + result = malloc(sizeof(MODULE_LIBRARY_HANDLE_DATA)); + if (result == NULL) + { + /*Codes_SRS_MODULE_LOADER_17_014: [If memory allocation is not successful, the load shall fail, and it shall return NULL.]*/ + LogError("ModuleLoader_Load() - malloc(sizeof(MODULE_LIBRARY_HANDLE_DATA)) failed"); + } + else + { + /* load the DLL */ + /* Codes_SRS_MODULE_LOADER_17_002: [ModuleLoader_Load shall load the library as a file, the filename given by the moduleLibraryFileName.]*/ + result->library = DynamicLibrary_LoadLibrary(moduleLibraryFileName); + if (result->library == NULL) + { + /* Codes_SRS_MODULE_LOADER_17_012: [If the attempt is not successful, the load shall fail, and it shall return NULL.]*/ + free(result); + result = NULL; + LogError("ModuleLoader_Load() - DynamicLibrary_LoadLibrary() returned NULL"); + } + else + { + /* Codes_SRS_MODULE_LOADER_17_003: [ModuleLoader_Load shall locate the function defined by MODULE_GETAPIS_NAME in the open library.] */ + pfModule_GetAPIS pfnGetAPIS = (pfModule_GetAPIS)DynamicLibrary_FindSymbol(result->library, MODULE_GETAPIS_NAME); + if (pfnGetAPIS == NULL) + { + /* Codes_SRS_MODULE_LOADER_17_013: [If locating the function is not successful, the load shall fail, and it shall return NULL.]*/ + DynamicLibrary_UnloadLibrary(result->library); + free(result); + result = NULL; + LogError("ModuleLoader_Load() - DynamicLibrary_FindSymbol() returned NULL"); + } + else + { + /* Codes_SRS_MODULE_LOADER_17_004: [ModuleLoader_Load shall call the function defined by MODULE_GETAPIS_NAME in the open library.]*/ + result->apis = pfnGetAPIS(); + + /* if "apis" is NULL then we have a misbehaving module */ + if (result->apis == NULL) + { + /* Codes_SRS_MODULE_LOADER_17_015: [If the get API call returns NULL, the load shall fail, and it shall return NULL.]*/ + DynamicLibrary_UnloadLibrary(result->library); + free(result); + result = NULL; + LogError("ModuleLoader_Load() - pfnGetAPIS() returned NULL"); + } + } + } + } + } + + /*Codes_SRS_MODULE_LOADER_17_006: [ModuleLoader_Load shall return a non-NULL handle to a MODULE_LIBRARY_DATA_TAG upon success.]*/ + return result; +} + +const MODULE_APIS* ModuleLoader_GetModuleAPIs(MODULE_LIBRARY_HANDLE moduleLibraryHandle) +{ + const MODULE_APIS* result; + + if (moduleLibraryHandle == NULL) + { + /*Codes_SRS_MODULE_LOADER_17_007: [ModuleLoader_GetModuleAPIs shall return NULL if the moduleLibraryHandle is NULL.]*/ + result = NULL; + LogError("ModuleLoader_GetModuleAPIs() - moduleLibraryHandle is NULL"); + } + else + { + /*Codes_SRS_MODULE_LOADER_17_008: [ModuleLoader_GetModuleAPIs shall return a valid pointer to MODULE_APIS on success.]*/ + MODULE_LIBRARY_HANDLE_DATA* loader_data = moduleLibraryHandle; + result = loader_data->apis; + } + + return result; +} + + +/*Codes_SRS_MODULE_LOADER_17_009: [ModuleLoader_Unload shall do nothing if the moduleLibraryHandle is NULL.]*/ +/*Codes_SRS_MODULE_LOADER_17_010: [ModuleLoader_Unload shall attempt to unload the library.]*/ +/*Codes_SRS_MODULE_LOADER_17_011: [ModuleLoader_UnLoad shall deallocate memory for the structure MODULE_LIBRARY_HANDLE.]*/ + +void ModuleLoader_Unload(MODULE_LIBRARY_HANDLE moduleLibraryHandle) +{ + if (moduleLibraryHandle != NULL) + { + MODULE_LIBRARY_HANDLE_DATA* loader_data = moduleLibraryHandle; + DynamicLibrary_UnloadLibrary(loader_data->library); + free(loader_data); + } + else + { + LogError("ModuleLoader_Unload() - moduleLibraryHandle is NULL"); + } +} diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt new file mode 100644 index 00000000..9973f984 --- /dev/null +++ b/core/tests/CMakeLists.txt @@ -0,0 +1,17 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) +#this is CMakeLists for the core tests folder + +add_subdirectory(gwmessage_unittests) +add_subdirectory(module_loader_unittests) +add_subdirectory(dynamic_library_unittests) +add_subdirectory(message_bus_unittests) +add_subdirectory(gateway_ll_unittests) +add_subdirectory(gateway_unittests) + +if(${run_e2e_tests}) + add_subdirectory(gw_e2etests) +endif() + diff --git a/core/tests/dynamic_library_unittests/CMakeLists.txt b/core/tests/dynamic_library_unittests/CMakeLists.txt new file mode 100644 index 00000000..aae1bf1c --- /dev/null +++ b/core/tests/dynamic_library_unittests/CMakeLists.txt @@ -0,0 +1,33 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for dynamic_library_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(testSuiteName dynamic_library_unittests) + +#setting the dynamic_loader file based on OS that it is used +if(WIN32) + set(${testSuiteName}_cpp_files +${testSuiteName}_windows.cpp) +set(${testSuiteName}_c_files +../../adapters/dynamic_library_windows.c +) +elseif(LINUX) + set(${testSuiteName}_cpp_files +${testSuiteName}_linux.cpp) +set(${testSuiteName}_c_files +../../adapters/dynamic_library_linux.c +) +endif() + +set(${testSuiteName}_h_files +../../inc/dynamic_library.h +) + +include_directories(${GW_INC}) + +add_definitions(-DGB_LIBRARY_INTERCEPT) + +build_test_artifacts(${testSuiteName} ON) \ No newline at end of file diff --git a/core/tests/dynamic_library_unittests/dynamic_library_unittests_linux.cpp b/core/tests/dynamic_library_unittests/dynamic_library_unittests_linux.cpp new file mode 100644 index 00000000..9baa456c --- /dev/null +++ b/core/tests/dynamic_library_unittests/dynamic_library_unittests_linux.cpp @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +#include "dynamic_library.h" +#include "gb_library.h" + +#include "azure_c_shared_utility/lock.h" + +#ifndef GB_LIBRARY_INTERCEPT +#error these unit tests require the symbol GB_LIBRARY_INTERCEPT to be defined +#else +extern "C" +{ + extern void *gb_dlopen(const char *__file, int __mode); + extern int gb_dlclose(void *__handle); + extern void *gb_dlsym(void * __handle, const char * __name); +} +#endif + + +#define LOAD_LIBRARY_RETURN (void*)1 +#define GET_PROC_ADDR_RETURN (void*)2 +#define HMODULE_HANDLE (void*)3 +#define LIBRARY_NAME "LIBRARY1" +#define SYMBOL_NAME "Symbol1" + +TYPED_MOCK_CLASS(CDynamicLibraryMocks, CGlobalMock) +{ +public: + MOCK_STATIC_METHOD_2(, void*, gb_dlopen, const char*, __file, int, __mode) + MOCK_METHOD_END(void*, (void*)LOAD_LIBRARY_RETURN) + + MOCK_STATIC_METHOD_1(, int, gb_dlclose, void*, library) + MOCK_METHOD_END(int, 1) + + MOCK_STATIC_METHOD_2(, void*, gb_dlsym, void*, library, const char*, symbolName) + MOCK_METHOD_END(void*, (void*)GET_PROC_ADDR_RETURN) + +}; + +DECLARE_GLOBAL_MOCK_METHOD_2(CDynamicLibraryMocks, , void*, gb_dlopen, const char*, __file, int, __mode); +DECLARE_GLOBAL_MOCK_METHOD_1(CDynamicLibraryMocks, , int, gb_dlclose, void*, library); +DECLARE_GLOBAL_MOCK_METHOD_2(CDynamicLibraryMocks, , void*, gb_dlsym, void*, library, const char*, symbolName); + + +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; +static MICROMOCK_MUTEX_HANDLE g_testByTest; + +BEGIN_TEST_SUITE(dynamic_library_unittests) + +TEST_SUITE_INITIALIZE(TestClassInitialize) +{ + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); +} + +TEST_SUITE_CLEANUP(TestClassCleanup) +{ + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); +} + +TEST_FUNCTION_INITIALIZE(TestMethodInitialize) +{ + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } +} + +TEST_FUNCTION_CLEANUP(TestMethodCleanup) +{ + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } +} + +// Tests_SRS_DYNAMIC_LIBRARY_17_001: [DynamicLibrary_LoadLibrary shall make the OS system call to load the named library, returning an opaque pointer as a library reference.] +TEST_FUNCTION(DynamicLibrary_LoadLibrary_returns_correct_value) +{ + CDynamicLibraryMocks mocks; + + ///arrange + const char* moduleFileName = LIBRARY_NAME; + + STRICT_EXPECTED_CALL(mocks, gb_dlopen(moduleFileName, RTLD_LAZY)) + .IgnoreArgument(2); + + + ///act + auto lib = DynamicLibrary_LoadLibrary(moduleFileName); + + ///assert + ASSERT_ARE_EQUAL(void_ptr, lib, LOAD_LIBRARY_RETURN); + + ///cleanup +} + +// Tests_SRS_DYNAMIC_LIBRARY_17_002: [DynamicLibrary_UnloadLibrary shall make the OS system call to unload the library referenced by libraryHandle.] +TEST_FUNCTION(DynamicLibrary_UnloadLibrary_Success) +{ + CDynamicLibraryMocks mocks; + + ///arrange + + void* library = HMODULE_HANDLE; + + STRICT_EXPECTED_CALL(mocks, gb_dlclose(library)); + + ///act + DynamicLibrary_UnloadLibrary(library); + + ///assert + + ///cleanup +} + +// Tests_SRS_DYNAMIC_LIBRARY_17_003: [DynamicLibrary_FindSymbol shall make the OS system call to look up symbolName in the library referenced by libraryHandle.] +TEST_FUNCTION(DynamicLibrary_FindSymbol_returns_correct_value) +{ + CDynamicLibraryMocks mocks; + + ///arrange + void* library = HMODULE_HANDLE; + const char * symbol = SYMBOL_NAME; + + STRICT_EXPECTED_CALL(mocks, gb_dlsym(library, symbol)); + + ///act + auto result = DynamicLibrary_FindSymbol(library, symbol); + + ///assert + ASSERT_ARE_EQUAL(void_ptr, result, GET_PROC_ADDR_RETURN); + + ///cleanup +} + +END_TEST_SUITE(dynamic_library_unittests) + + + + diff --git a/core/tests/dynamic_library_unittests/dynamic_library_unittests_windows.cpp b/core/tests/dynamic_library_unittests/dynamic_library_unittests_windows.cpp new file mode 100644 index 00000000..bb55cc43 --- /dev/null +++ b/core/tests/dynamic_library_unittests/dynamic_library_unittests_windows.cpp @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +#include "dynamic_library.h" + +#include "azure_c_shared_utility/lock.h" + +#ifndef GB_LIBRARY_INTERCEPT +#error these unit tests require the symbol GB_LIBRARY_INTERCEPT to be defined +#else +extern "C" +{ + extern void* gb_LoadLibraryA(const char* dynamicLibraryFileName); + extern int gb_FreeLibrary(void* library); + extern void* gb_GetProcAddress(void* library, const char* symbolName); + extern DWORD gb_GetLastError(); + extern DWORD gb_GetCurrentDirectoryA(DWORD nBufferLength, char* lpBuffer); + +} +#endif + + +#define LOAD_LIBRARY_RETURN (void*)1 +#define GET_PROC_ADDR_RETURN (void*)2 +#define HMODULE_HANDLE (void*)3 +#define LIBRARY_NAME "LIBRARY1" +#define SYMBOL_NAME "Symbol1" + + +TYPED_MOCK_CLASS(CDynamicLibraryMocks, CGlobalMock) +{ +public: + MOCK_STATIC_METHOD_1(, void*, gb_LoadLibraryA, const char*, dynamicLibraryFileName) + MOCK_METHOD_END(void*, (void*)LOAD_LIBRARY_RETURN) + + MOCK_STATIC_METHOD_1(, int, gb_FreeLibrary, void*, library) + MOCK_METHOD_END(int, 1) + + MOCK_STATIC_METHOD_2(, void*, gb_GetProcAddress, void*, library, const char*, symbolName) + MOCK_METHOD_END(void*, (void*)GET_PROC_ADDR_RETURN) + + MOCK_STATIC_METHOD_0(, DWORD, gb_GetLastError) + MOCK_METHOD_END(DWORD, 0) + + MOCK_STATIC_METHOD_2(, DWORD, gb_GetCurrentDirectoryA, DWORD, nBufferLength, char*, lpBuffer) + MOCK_METHOD_END(DWORD, 0) + +}; +DECLARE_GLOBAL_MOCK_METHOD_1(CDynamicLibraryMocks, , void*, gb_LoadLibraryA, const char*, dynamicLibraryFileName); +DECLARE_GLOBAL_MOCK_METHOD_1(CDynamicLibraryMocks, , int, gb_FreeLibrary, void*, library); +DECLARE_GLOBAL_MOCK_METHOD_2(CDynamicLibraryMocks, , void*, gb_GetProcAddress, void*, library, const char*, symbolName); +DECLARE_GLOBAL_MOCK_METHOD_0(CDynamicLibraryMocks, , DWORD, gb_GetLastError); +DECLARE_GLOBAL_MOCK_METHOD_2(CDynamicLibraryMocks, , DWORD, gb_GetCurrentDirectoryA, DWORD, nBufferLength, char*, lpBuffer); + + +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; +static MICROMOCK_MUTEX_HANDLE g_testByTest; + +BEGIN_TEST_SUITE(dynamic_library_unittests) + +TEST_SUITE_INITIALIZE(TestClassInitialize) +{ + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); +} + +TEST_SUITE_CLEANUP(TestClassCleanup) +{ + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); +} + +TEST_FUNCTION_INITIALIZE(TestMethodInitialize) +{ + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } +} + +TEST_FUNCTION_CLEANUP(TestMethodCleanup) +{ + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } +} + +// Tests_SRS_DYNAMIC_LIBRARY_17_001: [DynamicLibrary_LoadLibrary shall make the OS system call to load the named library, returning an opaque pointer as a library reference.] +TEST_FUNCTION(DynamicLibrary_LoadLibrary_returns_correct_value) +{ + CDynamicLibraryMocks mocks; + + ///arrange + const char* moduleFileName = LIBRARY_NAME; + + STRICT_EXPECTED_CALL(mocks, gb_LoadLibraryA(moduleFileName)); + + + ///act + auto lib = DynamicLibrary_LoadLibrary(moduleFileName); + + ///assert + ASSERT_ARE_EQUAL(void_ptr, lib, LOAD_LIBRARY_RETURN); + + ///cleanup +} + +// Tests_SRS_DYNAMIC_LIBRARY_17_002: [DynamicLibrary_UnloadLibrary shall make the OS system call to unload the library referenced by libraryHandle.] +TEST_FUNCTION(DynamicLibrary_UnloadLibrary_Success) +{ + CDynamicLibraryMocks mocks; + + ///arrange + + HMODULE library = (HMODULE)HMODULE_HANDLE; + + STRICT_EXPECTED_CALL(mocks, gb_FreeLibrary(library)); + + ///act + DynamicLibrary_UnloadLibrary(library); + + ///assert + + ///cleanup +} + +// Tests_SRS_DYNAMIC_LIBRARY_17_003: [DynamicLibrary_FindSymbol shall make the OS system call to look up symbolName in the library referenced by libraryHandle.] +TEST_FUNCTION(DynamicLibrary_FindSymbol_returns_correct_value) +{ + CDynamicLibraryMocks mocks; + + ///arrange + HMODULE library = (HMODULE)HMODULE_HANDLE; + const char * symbol = SYMBOL_NAME; + + STRICT_EXPECTED_CALL(mocks, gb_GetProcAddress(library, symbol)); + + ///act + auto result = DynamicLibrary_FindSymbol(library, symbol); + + ///assert + ASSERT_ARE_EQUAL(void_ptr, result, GET_PROC_ADDR_RETURN); + + ///cleanup +} + +END_TEST_SUITE(dynamic_library_unittests) + + + + diff --git a/core/tests/dynamic_library_unittests/main.c b/core/tests/dynamic_library_unittests/main.c new file mode 100644 index 00000000..9521eba3 --- /dev/null +++ b/core/tests/dynamic_library_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(dynamic_library_unittests, failedTestCount); + return failedTestCount; +} diff --git a/core/tests/gateway_ll_unittests/CMakeLists.txt b/core/tests/gateway_ll_unittests/CMakeLists.txt new file mode 100644 index 00000000..856b9099 --- /dev/null +++ b/core/tests/gateway_ll_unittests/CMakeLists.txt @@ -0,0 +1,23 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#This is the CMakeLists.txt for gateway_ll_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(testSuite gateway_ll_unittests) +set(${testSuite}_cpp_files + ${testSuite}.cpp +) + +set(${testSuite}_c_files + ../../src/gateway_ll.c +) + +set(${testSuite}_h_files + ../../inc/gateway_ll.h +) + +include_directories(${GW_INC}) + +build_test_artifacts(${testSuite} ON) \ No newline at end of file diff --git a/core/tests/gateway_ll_unittests/gateway_ll_unittests.cpp b/core/tests/gateway_ll_unittests/gateway_ll_unittests.cpp new file mode 100644 index 00000000..15c52854 --- /dev/null +++ b/core/tests/gateway_ll_unittests/gateway_ll_unittests.cpp @@ -0,0 +1,1346 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" +#include "azure_c_shared_utility/lock.h" + +#include "gateway_ll.h" +#include "message_bus.h" +#include "module_loader.h" + +#define DUMMY_LIBRARY_PATH "x.dll" + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ + +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit +#include "vector.c" + +}; + +static size_t currentmalloc_call; +static size_t whenShallmalloc_fail; + +static size_t currentMessageBus_AddModule_call; +static size_t whenShallMessageBus_AddModule_fail; +static size_t currentMessageBus_RemoveModule_call; +static size_t whenShallMessageBus_RemoveModule_fail; +static size_t currentMessageBus_Create_call; +static size_t whenShallMessageBus_Create_fail; +static size_t currentMessageBus_module_count; +static size_t currentMessageBus_ref_count; + +static size_t currentModuleLoader_Load_call; +static size_t whenShallModuleLoader_Load_fail; + +static size_t currentModule_Create_call; +static size_t currentModule_Destroy_call; + +static size_t currentVECTOR_create_call; +static size_t whenShallVECTOR_create_fail; +static size_t currentVECTOR_push_back_call; +static size_t whenShallVECTOR_push_back_fail; +static size_t currentVECTOR_find_if_call; +static size_t whenShallVECTOR_find_if_fail; + +static MODULE_APIS dummyAPIs; + +TYPED_MOCK_CLASS(CGatewayLLMocks, CGlobalMock) +{ +public: + MOCK_STATIC_METHOD_2(, MODULE_HANDLE, mock_Module_Create, MESSAGE_BUS_HANDLE, busHandle, const void*, configuration) + currentModule_Create_call++; + MODULE_HANDLE result1; + if (configuration != NULL && *((bool*)configuration) == false) + { + result1 = NULL; + } + else + { + result1 = (MODULE_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(currentModule_Create_call); + } + MOCK_METHOD_END(MODULE_HANDLE, result1); + + MOCK_STATIC_METHOD_1(, void, mock_Module_Destroy, MODULE_HANDLE, moduleHandle) + currentModule_Destroy_call++; + BASEIMPLEMENTATION::gballoc_free(moduleHandle); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_2(, void, mock_Module_Receive, MODULE_HANDLE, moduleHandle, MESSAGE_HANDLE, messageHandle) + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_1(, void, MessageBus_DecRef, MESSAGE_BUS_HANDLE, bus) + if (currentMessageBus_ref_count > 0) + { + --currentMessageBus_ref_count; + if (currentMessageBus_ref_count == 0) + { + BASEIMPLEMENTATION::gballoc_free(bus); + } + } + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_1(, void, MessageBus_IncRef, MESSAGE_BUS_HANDLE, bus) + ++currentMessageBus_ref_count; + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_0(, MESSAGE_BUS_HANDLE, MessageBus_Create) + MESSAGE_BUS_HANDLE result1; + currentMessageBus_Create_call++; + if (whenShallMessageBus_Create_fail >= 0 && whenShallMessageBus_Create_fail == currentMessageBus_Create_call) + { + result1 = NULL; + } + else + { + ++currentMessageBus_ref_count; + result1 = (MESSAGE_BUS_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(1); + } + MOCK_METHOD_END(MESSAGE_BUS_HANDLE, result1); + + MOCK_STATIC_METHOD_1(, void, MessageBus_Destroy, MESSAGE_BUS_HANDLE, bus) + if (currentMessageBus_ref_count > 0) + { + --currentMessageBus_ref_count; + if (currentMessageBus_ref_count == 0) + { + BASEIMPLEMENTATION::gballoc_free(bus); + } + } + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_3(, MESSAGE_BUS_RESULT, MessageBus_AddModule, MESSAGE_BUS_HANDLE, handle, MODULE_HANDLE, module, const MODULE_APIS*, module_apis) + currentMessageBus_AddModule_call++; + MESSAGE_BUS_RESULT result1 = MESSAGE_BUS_ERROR; + if (handle != NULL && module != NULL) + { + if (whenShallMessageBus_AddModule_fail != currentMessageBus_AddModule_call) + { + ++currentMessageBus_module_count; + result1 = MESSAGE_BUS_OK; + } + } + MOCK_METHOD_END(MESSAGE_BUS_RESULT, result1); + + MOCK_STATIC_METHOD_2(, MESSAGE_BUS_RESULT, MessageBus_RemoveModule, MESSAGE_BUS_HANDLE, handle, MODULE_HANDLE, module) + currentMessageBus_RemoveModule_call++; + MESSAGE_BUS_RESULT result1 = MESSAGE_BUS_ERROR; + if (handle != NULL && module != NULL && currentMessageBus_module_count > 0 && whenShallMessageBus_RemoveModule_fail != currentMessageBus_RemoveModule_call) + { + --currentMessageBus_module_count; + result1 = MESSAGE_BUS_OK; + } + MOCK_METHOD_END(MESSAGE_BUS_RESULT, result1); + + MOCK_STATIC_METHOD_1(, MODULE_LIBRARY_HANDLE, ModuleLoader_Load, const char*, moduleLibraryFileName) + currentModuleLoader_Load_call++; + MODULE_LIBRARY_HANDLE handle = NULL; + if (whenShallModuleLoader_Load_fail >= 0 && whenShallModuleLoader_Load_fail != currentModuleLoader_Load_call) + { + handle = (MODULE_LIBRARY_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(1); + } + MOCK_METHOD_END(MODULE_LIBRARY_HANDLE, handle); + + MOCK_STATIC_METHOD_1(, const MODULE_APIS*, ModuleLoader_GetModuleAPIs, MODULE_LIBRARY_HANDLE, module_library_handle) + const MODULE_APIS* apis = &dummyAPIs; + MOCK_METHOD_END(const MODULE_APIS*, apis); + + MOCK_STATIC_METHOD_1(, void, ModuleLoader_Unload, MODULE_LIBRARY_HANDLE, moduleLibraryHandle) + BASEIMPLEMENTATION::gballoc_free(moduleLibraryHandle); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_1(, VECTOR_HANDLE, VECTOR_create, size_t, elementSize) + currentVECTOR_create_call++; + VECTOR_HANDLE vector = NULL; + if (whenShallVECTOR_create_fail != currentVECTOR_create_call) + { + vector = BASEIMPLEMENTATION::VECTOR_create(elementSize); + } + MOCK_METHOD_END(VECTOR_HANDLE, vector); + + MOCK_STATIC_METHOD_1(, void, VECTOR_destroy, VECTOR_HANDLE, handle) + BASEIMPLEMENTATION::VECTOR_destroy(handle); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_3(, int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements) + currentVECTOR_push_back_call++; + int result1 = -1; + if (whenShallVECTOR_push_back_fail != currentVECTOR_push_back_call) + { + result1 = BASEIMPLEMENTATION::VECTOR_push_back(handle, elements, numElements); + } + MOCK_METHOD_END(int, result1); + + MOCK_STATIC_METHOD_3(, void, VECTOR_erase, VECTOR_HANDLE, handle, void*, elements, size_t, index) + BASEIMPLEMENTATION::VECTOR_erase(handle, elements, index); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_2(, void*, VECTOR_element, const VECTOR_HANDLE, handle, size_t, index) + auto element = BASEIMPLEMENTATION::VECTOR_element(handle, index); + MOCK_METHOD_END(void*, element); + + MOCK_STATIC_METHOD_1(, void*, VECTOR_front, const VECTOR_HANDLE, handle) + auto element = BASEIMPLEMENTATION::VECTOR_front(handle); + MOCK_METHOD_END(void*, element); + + MOCK_STATIC_METHOD_1(, size_t, VECTOR_size, const VECTOR_HANDLE, handle) + auto size = BASEIMPLEMENTATION::VECTOR_size(handle); + MOCK_METHOD_END(size_t, size); + + MOCK_STATIC_METHOD_3(, void*, VECTOR_find_if, const VECTOR_HANDLE, handle, PREDICATE_FUNCTION, pred, const void*, value) + currentVECTOR_find_if_call++; + void* element = NULL; + if (whenShallVECTOR_find_if_fail != currentVECTOR_find_if_call) + { + element = BASEIMPLEMENTATION::VECTOR_find_if(handle, pred, value); + } + MOCK_METHOD_END(void*, element); + + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2; + currentmalloc_call++; + if (whenShallmalloc_fail>0) + { + if (currentmalloc_call == whenShallmalloc_fail) + { + result2 = NULL; + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + } + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + } + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_2(, void*, gballoc_realloc, void*, ptr, size_t, size) + MOCK_METHOD_END(void*, BASEIMPLEMENTATION::gballoc_realloc(ptr, size)); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + +}; + +DECLARE_GLOBAL_MOCK_METHOD_2(CGatewayLLMocks, , MODULE_HANDLE, mock_Module_Create, MESSAGE_BUS_HANDLE, busHandle, const void*, configuration); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , void, mock_Module_Destroy, MODULE_HANDLE, moduleHandle); +DECLARE_GLOBAL_MOCK_METHOD_2(CGatewayLLMocks, , void, mock_Module_Receive, MODULE_HANDLE, moduleHandle, MESSAGE_HANDLE, messageHandle); + +DECLARE_GLOBAL_MOCK_METHOD_0(CGatewayLLMocks, , MESSAGE_BUS_HANDLE, MessageBus_Create); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , void, MessageBus_Destroy, MESSAGE_BUS_HANDLE, bus); +DECLARE_GLOBAL_MOCK_METHOD_3(CGatewayLLMocks, , MESSAGE_BUS_RESULT, MessageBus_AddModule, MESSAGE_BUS_HANDLE, handle, MODULE_HANDLE, module, const MODULE_APIS*, module_apis); +DECLARE_GLOBAL_MOCK_METHOD_2(CGatewayLLMocks, , MESSAGE_BUS_RESULT, MessageBus_RemoveModule, MESSAGE_BUS_HANDLE, handle, MODULE_HANDLE, module); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , void, MessageBus_IncRef, MESSAGE_BUS_HANDLE, bus); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , void, MessageBus_DecRef, MESSAGE_BUS_HANDLE, bus); + +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , MODULE_LIBRARY_HANDLE, ModuleLoader_Load, const char*, moduleLibraryFileName); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , const MODULE_APIS*, ModuleLoader_GetModuleAPIs, MODULE_LIBRARY_HANDLE, module_library_handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , void, ModuleLoader_Unload, MODULE_LIBRARY_HANDLE, moduleLibraryHandle); + +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , VECTOR_HANDLE, VECTOR_create, size_t, elementSize); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , void, VECTOR_destroy, VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CGatewayLLMocks, , int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements); +DECLARE_GLOBAL_MOCK_METHOD_3(CGatewayLLMocks, , void, VECTOR_erase, VECTOR_HANDLE, handle, void*, elements, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_2(CGatewayLLMocks, , void*, VECTOR_element, const VECTOR_HANDLE, handle, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , void*, VECTOR_front, const VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , size_t, VECTOR_size, const VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CGatewayLLMocks, , void*, VECTOR_find_if, const VECTOR_HANDLE, handle, PREDICATE_FUNCTION, pred, const void*, value); + + +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_2(CGatewayLLMocks, , void*, gballoc_realloc, void*, ptr, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayLLMocks, , void, gballoc_free, void*, ptr) + +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; +static MICROMOCK_MUTEX_HANDLE g_testByTest; + +static GATEWAY_PROPERTIES* dummyProps; + +BEGIN_TEST_SUITE(gateway_ll_unittests) + +TEST_SUITE_INITIALIZE(TestClassInitialize) +{ + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); +} + +TEST_SUITE_CLEANUP(TestClassCleanup) +{ + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); +} + +TEST_FUNCTION_INITIALIZE(TestMethodInitialize) +{ + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + currentmalloc_call = 0; + whenShallmalloc_fail = 0; + + currentMessageBus_AddModule_call = 0; + whenShallMessageBus_AddModule_fail = 0; + currentMessageBus_RemoveModule_call = 0; + whenShallMessageBus_RemoveModule_fail = 0; + currentMessageBus_Create_call = 0; + whenShallMessageBus_Create_fail = 0; + currentMessageBus_module_count = 0; + currentMessageBus_ref_count = 0; + + currentModuleLoader_Load_call = 0; + whenShallModuleLoader_Load_fail = 0; + + currentModule_Create_call = 0; + currentModule_Destroy_call = 0; + + currentVECTOR_create_call = 0; + whenShallVECTOR_create_fail = 0; + currentVECTOR_push_back_call = 0; + whenShallVECTOR_push_back_fail = 0; + currentVECTOR_find_if_call = 0; + whenShallVECTOR_find_if_fail = 0; + + dummyAPIs = { + mock_Module_Create, + mock_Module_Destroy, + mock_Module_Receive + }; + + GATEWAY_PROPERTIES_ENTRY dummyEntry = { + "dummy module", + DUMMY_LIBRARY_PATH, + NULL + }; + + dummyProps = (GATEWAY_PROPERTIES*)malloc(sizeof(GATEWAY_PROPERTIES)); + dummyProps->gateway_properties_entries = BASEIMPLEMENTATION::VECTOR_create(sizeof(GATEWAY_PROPERTIES_ENTRY)); + BASEIMPLEMENTATION::VECTOR_push_back(dummyProps->gateway_properties_entries, &dummyEntry, 1); +} + +TEST_FUNCTION_CLEANUP(TestMethodCleanup) +{ + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + currentmalloc_call = 0; + whenShallmalloc_fail = 0; + + currentMessageBus_Create_call = 0; + whenShallMessageBus_Create_fail = 0; + + BASEIMPLEMENTATION::VECTOR_destroy(dummyProps->gateway_properties_entries); + free(dummyProps); +} + +/*Tests_SRS_GATEWAY_LL_14_001: [This function shall create a GATEWAY_HANDLE representing the newly created gateway.]*/ +/*Tests_SRS_GATEWAY_LL_14_003: [This function shall create a new MESSAGE_BUS_HANDLE for the gateway representing this gateway's message bus. ]*/ +/*Tests_SRS_GATEWAY_LL_14_033: [ The function shall create a vector to store each MODULE_DATA. ]*/ +TEST_FUNCTION(Gateway_LL_Create_Creates_Handle_Success) +{ + //Arrange + CGatewayLLMocks mocks; + + //Expectations + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Create()); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + //Act + GATEWAY_HANDLE gateway = Gateway_LL_Create(NULL); + + //Assert + ASSERT_IS_NOT_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gateway); +} + +/*Tests_SRS_GATEWAY_LL_14_002: [This function shall return NULL upon any memory allocation failure.]*/ +TEST_FUNCTION(Gateway_LL_Create_Creates_Handle_Malloc_Failure) +{ + //Arrange + CGatewayLLMocks mocks; + + //Expectations + whenShallmalloc_fail = 1; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + //Act + GATEWAY_HANDLE gateway = Gateway_LL_Create(NULL); + + //Assert + ASSERT_IS_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + //Nothing to cleanup +} + +/*Tests_SRS_GATEWAY_LL_14_004: [ This function shall return NULL if a MESSAGE_BUS_HANDLE cannot be created. ]*/ +TEST_FUNCTION(Gateway_LL_Create_Cannot_Create_MessageBus_Handle_Failure) +{ + //Arrange + CGatewayLLMocks mocks; + + //Expectations + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + whenShallMessageBus_Create_fail = 1; + STRICT_EXPECTED_CALL(mocks, MessageBus_Create()); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + GATEWAY_HANDLE gateway = Gateway_LL_Create(NULL); + + //Assert + ASSERT_IS_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + //Nothing to cleanup +} + +/*Tests_SRS_GATEWAY_LL_14_034: [ This function shall return NULL if a VECTOR_HANDLE cannot be created. ]*/ +/*Tests_SRS_GATEWAY_LL_14_035: [ This function shall destroy the previously created MESSAGE_BUS_HANDLE and free the GATEWAY_HANDLE if the VECTOR_HANDLE cannot be created. ]*/ +TEST_FUNCTION(Gateway_LL_Create_VECTOR_Create_Fails) +{ + //Arrange + CGatewayLLMocks mocks; + + //Expectations + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, MessageBus_Create()); + + whenShallVECTOR_create_fail = 1; + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, MessageBus_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + GATEWAY_HANDLE gateway = Gateway_LL_Create(NULL); + + //Assert + ASSERT_IS_NULL(gateway); + + //Cleanup + //Nothing to cleanup +} + +TEST_FUNCTION(Gateway_LL_Create_VECTOR_push_back_Fails_To_Add_All_Modules_In_Props) +{ + //Arrange + CGatewayLLMocks mocks; + + //Add another entry to the properties + GATEWAY_PROPERTIES_ENTRY dummyEntry2 = { + "dummy module 2", + "x2.dll", + NULL + }; + + BASEIMPLEMENTATION::VECTOR_push_back(dummyProps->gateway_properties_entries, &dummyEntry2, 1); + + //Expectations + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Create()); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(dummyProps->gateway_properties_entries)); + + //Adding module 1 (Success) + STRICT_EXPECTED_CALL(mocks, VECTOR_element(dummyProps->gateway_properties_entries, 0)); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Create(IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_AddModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG, &dummyAPIs)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_IncRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + //Adding module 2 (Failure) + STRICT_EXPECTED_CALL(mocks, VECTOR_element(dummyProps->gateway_properties_entries, 1)); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Create(IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_AddModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG, &dummyAPIs)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_IncRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + whenShallVECTOR_push_back_fail = 2; + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_RemoveModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_DecRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Removing previous module + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_front(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_RemoveModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_DecRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_erase(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + GATEWAY_HANDLE gateway = Gateway_LL_Create(dummyProps); + + ASSERT_IS_NULL(gateway); + ASSERT_ARE_EQUAL(size_t, 0, currentMessageBus_module_count); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gateway); + +} + +/*Tests_SRS_GATEWAY_LL_14_036: [ If any MODULE_HANDLE is unable to be created from a GATEWAY_PROPERTIES_ENTRY the GATEWAY_HANDLE will be destroyed. ]*/ +TEST_FUNCTION(Gateway_LL_Create_MessageBus_AddModule_Fails_To_Add_All_Modules_In_Props) +{ + //Arrange + CGatewayLLMocks mocks; + + //Add another entry to the properties + GATEWAY_PROPERTIES_ENTRY dummyEntry2 = { + "dummy module 2", + "x2.dll", + NULL + }; + + BASEIMPLEMENTATION::VECTOR_push_back(dummyProps->gateway_properties_entries, &dummyEntry2, 1); + + //Expectations + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Create()); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(dummyProps->gateway_properties_entries)); + + //Adding module 1 (Success) + STRICT_EXPECTED_CALL(mocks, VECTOR_element(dummyProps->gateway_properties_entries, 0)); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Create(IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_AddModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG, &dummyAPIs)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_IncRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + //Adding module 2 (Failure) + STRICT_EXPECTED_CALL(mocks, VECTOR_element(dummyProps->gateway_properties_entries, 1)); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Create(IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1); + whenShallMessageBus_AddModule_fail = 2; + STRICT_EXPECTED_CALL(mocks, MessageBus_AddModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG, &dummyAPIs)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Removing previous module + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_front(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_RemoveModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_DecRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_erase(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + GATEWAY_HANDLE gateway = Gateway_LL_Create(dummyProps); + + ASSERT_IS_NULL(gateway); + ASSERT_ARE_EQUAL(size_t, 0, currentMessageBus_module_count); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gateway); + +} + +/*Tests_SRS_GATEWAY_LL_14_009: [ The function shall use each GATEWAY_PROPERTIES_ENTRY use each of GATEWAY_PROPERTIES's gateway_properties_entries to create and add a module to the GATEWAY_HANDLE message bus. ]*/ +TEST_FUNCTION(Gateway_LL_Create_Adds_All_Modules_In_Props_Success) +{ + //Arrange + CGatewayLLMocks mocks; + + //Add another entry to the properties + GATEWAY_PROPERTIES_ENTRY dummyEntry2 = { + "dummy module 2", + "x2.dll", + NULL + }; + + BASEIMPLEMENTATION::VECTOR_push_back(dummyProps->gateway_properties_entries, &dummyEntry2, 1); + + //Expectations + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Create()); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(dummyProps->gateway_properties_entries)); + + //Adding module 1 (Failure) + STRICT_EXPECTED_CALL(mocks, VECTOR_element(dummyProps->gateway_properties_entries, 0)); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Create(IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_AddModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG, &dummyAPIs)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_IncRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + //Adding module 2 (Success) + STRICT_EXPECTED_CALL(mocks, VECTOR_element(dummyProps->gateway_properties_entries, 1)); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Create(IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_AddModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG, &dummyAPIs)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_IncRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + //Act + GATEWAY_HANDLE gateway = Gateway_LL_Create(dummyProps); + + //Assert + ASSERT_IS_NOT_NULL(gateway); + ASSERT_ARE_EQUAL(size_t, 2, currentMessageBus_module_count); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gateway); +} + +/*Tests_SRS_GATEWAY_LL_14_005: [ If gw is NULL the function shall do nothing. ]*/ +TEST_FUNCTION(Gateway_LL_Destroy_Does_Nothing_If_NULL) +{ + //Arrange + CGatewayLLMocks mocks; + + GATEWAY_HANDLE gateway = NULL; + + //Act + Gateway_LL_Destroy(gateway); + + //Assert + mocks.AssertActualAndExpectedCalls(); +} + +/*Tests_SRS_GATEWAY_LL_14_037: [ If GATEWAY_HANDLE_DATA's message bus cannot unlink module, the function shall log the error and continue unloading the module from the GATEWAY_HANDLE. ]*/ +TEST_FUNCTION(Gateway_LL_Destroy_Continues_Unloading_If_MessageBus_RemoveModule_Fails) +{ + //Arrange + CGatewayLLMocks mocks; + + //Add another entry to the properties + GATEWAY_PROPERTIES_ENTRY dummyEntry2 = { + "dummy module 2", + "x2.dll", + NULL + }; + + BASEIMPLEMENTATION::VECTOR_push_back(dummyProps->gateway_properties_entries, &dummyEntry2, 1); + + GATEWAY_HANDLE gateway = Gateway_LL_Create(dummyProps); + mocks.ResetAllCalls(); + + //Expectations + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_front(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + whenShallMessageBus_RemoveModule_fail = 1; + STRICT_EXPECTED_CALL(mocks, MessageBus_RemoveModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_DecRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_erase(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_front(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_RemoveModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_DecRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_erase(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + Gateway_LL_Destroy(gateway); + + //Assert + mocks.AssertActualAndExpectedCalls(); +} + +/*Tests_SRS_GATEWAY_LL_14_028: [ The function shall remove each module in GATEWAY_HANDLE_DATA's modules vector and destroy GATEWAY_HANDLE_DATA's modules. ]*/ +/*Tests_SRS_GATEWAY_LL_14_006: [ The function shall destroy the GATEWAY_HANDLE_DATA's bus MESSAGE_BUS_HANDLE. ]*/ +TEST_FUNCTION(Gateway_LL_Destroy_Removes_All_Modules_And_Destroys_Vector_Success) +{ + //Arrange + CGatewayLLMocks mocks; + + //Add another entry to the properties + GATEWAY_PROPERTIES_ENTRY dummyEntry2 = { + "dummy module 2", + "x2.dll", + NULL + }; + + BASEIMPLEMENTATION::VECTOR_push_back(dummyProps->gateway_properties_entries, &dummyEntry2, 1); + + GATEWAY_HANDLE gateway = Gateway_LL_Create(dummyProps); + mocks.ResetAllCalls(); + + //Gateway_LL_Destroy Expectations + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_front(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_RemoveModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_DecRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_erase(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_front(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_RemoveModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_DecRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_erase(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + Gateway_LL_Destroy(gateway); + + //Assert + ASSERT_ARE_EQUAL(size_t, 0, currentMessageBus_module_count); + mocks.AssertActualAndExpectedCalls(); +} + +/*Tests_SRS_GATEWAY_LL_14_011: [ If gw, entry, or GATEWAY_PROPERTIES_ENTRY's module_path is NULL the function shall return NULL. ]*/ +TEST_FUNCTION(Gateway_LL_AddModule_Returns_Null_For_Null_Gateway) +{ + //Arrange + CGatewayLLMocks mocks; + + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + mocks.ResetAllCalls(); + + //Act + MODULE_HANDLE handle0 = Gateway_LL_AddModule(NULL, (GATEWAY_PROPERTIES_ENTRY*)BASEIMPLEMENTATION::VECTOR_front(dummyProps->gateway_properties_entries)); + + //Assert + ASSERT_IS_NULL(handle0); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_011: [ If gw, entry, or GATEWAY_PROPERTIES_ENTRY's module_path is NULL the function shall return NULL. ]*/ +TEST_FUNCTION(Gateway_LL_AddModule_Returns_Null_For_Null_Module) +{ + //Arrange + CGatewayLLMocks mocks; + + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + mocks.ResetAllCalls(); + + //Act + MODULE_HANDLE handle1 = Gateway_LL_AddModule(gw, NULL); + + //Assert + ASSERT_IS_NULL(handle1); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_011: [ If gw, entry, or GATEWAY_PROPERTIES_ENTRY's module_path is NULL the function shall return NULL. ]*/ +TEST_FUNCTION(Gateway_LL_AddModule_Returns_Null_For_Null_Params) +{ + //Arrange + CGatewayLLMocks mocks; + + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + mocks.ResetAllCalls(); + + //Act + GATEWAY_PROPERTIES_ENTRY* entry = (GATEWAY_PROPERTIES_ENTRY*)BASEIMPLEMENTATION::VECTOR_front(dummyProps->gateway_properties_entries); + entry->module_path = NULL; + MODULE_HANDLE handle2 = Gateway_LL_AddModule(gw, (GATEWAY_PROPERTIES_ENTRY*)BASEIMPLEMENTATION::VECTOR_front(dummyProps->gateway_properties_entries)); + + //Assert + ASSERT_IS_NULL(handle2); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_012: [ The function shall load the module located at GATEWAY_PROPERTIES_ENTRY's module_path into a MODULE_LIBRARY_HANDLE. ]*/ +/*Tests_SRS_GATEWAY_LL_14_013: [ The function shall get the const MODULE_APIS* from the MODULE_LIBRARY_HANDLE. ]*/ +/*Tests_SRS_GATEWAY_LL_14_017: [ The function shall link the module to the GATEWAY_HANDLE_DATA's bus using a call to MessageBus_AddModule. ]*/ +/*Tests_SRS_GATEWAY_LL_14_029: [ The function shall create a new MODULE_DATA containting the MODULE_HANDLE and MODULE_LIBRARY_HANDLE if the module was successfully linked to the message bus. ]*/ +/*Tests_SRS_GATEWAY_LL_14_032: [ The function shall add the new MODULE_DATA to GATEWAY_HANDLE_DATA's modules if the module was successfully linked to the message bus. ]*/ +/*Tests_SRS_GATEWAY_LL_14_019: [ The function shall return the newly created MODULE_HANDLE only if each API call returns successfully. ]*/ +/*Tests_SRS_GATEWAY_LL_14_039: [ The function shall increment the MESSAGE_BUS_HANDLE reference count if the MODULE_HANDLE was successfully linked to the GATEWAY_HANDLE_DATA's bus. ]*/ +TEST_FUNCTION(Gateway_LL_AddModule_Loads_Module_From_Library_Path) +{ + //Arrange + CGatewayLLMocks mocks; + + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + mocks.ResetAllCalls(); + + //Expectations + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(DUMMY_LIBRARY_PATH)); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_AddModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, MessageBus_IncRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + //Act + MODULE_HANDLE handle = Gateway_LL_AddModule(gw, (GATEWAY_PROPERTIES_ENTRY*)BASEIMPLEMENTATION::VECTOR_front(dummyProps->gateway_properties_entries)); + + //Assert + ASSERT_IS_NOT_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_031: [ If unsuccessful, the function shall return NULL. ]*/ +TEST_FUNCTION(Gateway_LL_AddModule_Fails) +{ + //Arrange + CGatewayLLMocks mocks; + + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + mocks.ResetActualCalls(); + + //Expectations + whenShallModuleLoader_Load_fail = 1; + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + MODULE_HANDLE handle = Gateway_LL_AddModule(gw, (GATEWAY_PROPERTIES_ENTRY*)BASEIMPLEMENTATION::VECTOR_front(dummyProps->gateway_properties_entries)); + + //Assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_015: [ The function shall use the MODULE_APIS to create a MODULE_HANDLE using the GATEWAY_PROPERTIES_ENTRY's module_properties. ]*/ +/*Tests_SRS_GATEWAY_LL_14_039: [ The function shall increment the MESSAGE_BUS_HANDLE reference count if the MODULE_HANDLE was successfully linked to the GATEWAY_HANDLE_DATA's bus. ]*/ +TEST_FUNCTION(Gateway_LL_AddModule_Creates_Module_Using_Module_Properties) +{ + //Arrange + CGatewayLLMocks mocks; + + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + mocks.ResetAllCalls(); + bool* properties = (bool*)malloc(sizeof(bool)); + *properties = true; + GATEWAY_PROPERTIES_ENTRY entry = { + "Test module", + DUMMY_LIBRARY_PATH, + properties + }; + + //Expectations + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Create(IGNORED_PTR_ARG, properties)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_AddModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, MessageBus_IncRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + //Act + MODULE_HANDLE handle = Gateway_LL_AddModule(gw, &entry); + + //Assert + ASSERT_IS_NOT_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); + free(properties); +} + +/*Tests_SRS_GATEWAY_LL_14_016: [ If the module creation is unsuccessful, the function shall return NULL. ]*/ +TEST_FUNCTION(Gateway_LL_AddModule_Module_Create_Fails) +{ + //Arrange + CGatewayLLMocks mocks; + + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + mocks.ResetAllCalls(); + //Setting this boolean to false will cause mock_Module_Create to fail + bool properties = false; + GATEWAY_PROPERTIES_ENTRY entry = { + "Test module", + DUMMY_LIBRARY_PATH, + &properties + }; + + //Expectations + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(DUMMY_LIBRARY_PATH)); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + MODULE_HANDLE handle = Gateway_LL_AddModule(gw, &entry); + + //Assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_018: [ If the message bus linking is unsuccessful, the function shall return NULL. ]*/ +TEST_FUNCTION(Gateway_LL_AddModule_MessageBus_AddModule_Fails) +{ + //Arrange + CGatewayLLMocks mocks; + + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + mocks.ResetAllCalls(); + + //Expectations + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + whenShallMessageBus_AddModule_fail = 1; + STRICT_EXPECTED_CALL(mocks, MessageBus_AddModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + + //Act + MODULE_HANDLE handle = Gateway_LL_AddModule(gw, (GATEWAY_PROPERTIES_ENTRY*)BASEIMPLEMENTATION::VECTOR_front(dummyProps->gateway_properties_entries)); + + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_030: [ If any internal API call is unsuccessful after a module is created, the library will be unloaded and the module destroyed. ]*/ +/*Tests_SRS_GATEWAY_LL_14_039: [The function shall increment the MESSAGE_BUS_HANDLE reference count if the MODULE_HANDLE was successfully linked to the GATEWAY_HANDLE_DATA's bus. ]*/ +TEST_FUNCTION(Gateway_LL_AddModule_Internal_API_Fail_Rollback_Module) +{ + //Arrange + CGatewayLLMocks mocks; + + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + mocks.ResetAllCalls(); + + //Expectations + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Load(DUMMY_LIBRARY_PATH)); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Create(IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_AddModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, MessageBus_IncRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + whenShallVECTOR_push_back_fail = 1; + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_RemoveModule(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MessageBus_DecRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + MODULE_HANDLE handle = Gateway_LL_AddModule(gw, (GATEWAY_PROPERTIES_ENTRY*)BASEIMPLEMENTATION::VECTOR_front(dummyProps->gateway_properties_entries)); + + ASSERT_IS_NULL(handle); + ASSERT_ARE_EQUAL(size_t, 0, currentMessageBus_module_count); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_020: [ If gw or module is NULL the function shall return. ]*/ +TEST_FUNCTION(Gateway_LL_RemoveModule_Does_Nothing_If_Gateway_NULL) +{ + //Arrange + CGatewayLLMocks mocks; + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + MODULE_HANDLE handle = Gateway_LL_AddModule(gw, (GATEWAY_PROPERTIES_ENTRY*)BASEIMPLEMENTATION::VECTOR_front(dummyProps->gateway_properties_entries)); + mocks.ResetAllCalls(); + + //Act + Gateway_LL_RemoveModule(NULL, NULL); + Gateway_LL_RemoveModule(NULL, handle); + + //Assert + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_020: [ If gw or module is NULL the function shall return. ]*/ +TEST_FUNCTION(Gateway_LL_RemoveModule_Does_Nothing_If_Module_NULL) +{ + //Arrange + CGatewayLLMocks mocks; + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + mocks.ResetAllCalls(); + + //Expectations + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + //Act + Gateway_LL_RemoveModule(gw, NULL); + + //Assert + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_023: [ The function shall locate the MODULE_DATA object in GATEWAY_HANDLE_DATA's modules containing module and return if it cannot be found. ]*/ +/*Tests_SRS_GATEWAY_LL_14_021: [ The function shall unlink module from the GATEWAY_HANDLE_DATA's bus MESSAGE_BUS_HANDLE. ]*/ +/*Tests_SRS_GATEWAY_LL_14_024: [ The function shall use the MODULE_DATA's module_library_handle to retrieve the MODULE_APIS and destroy module. ]*/ +/*Tests_SRS_GATEWAY_LL_14_025: [ The function shall unload MODULE_DATA's module_library_handle. ]*/ +/*Tests_SRS_GATEWAY_LL_14_026: [ The function shall remove that MODULE_DATA from GATEWAY_HANDLE_DATA's modules. ]*/ +/*Tests_SRS_GATEWAY_LL_14_038: [ The function shall decrement the MESSAGE_BUS_HANDLE reference count. ]*/ +TEST_FUNCTION(Gateway_LL_RemoveModule_Finds_Module_Data_Success) +{ + //Arrange + CGatewayLLMocks mocks; + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + MODULE_HANDLE handle = Gateway_LL_AddModule(gw, (GATEWAY_PROPERTIES_ENTRY*)BASEIMPLEMENTATION::VECTOR_front(dummyProps->gateway_properties_entries)); + mocks.ResetAllCalls(); + + //Expectations + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, MessageBus_RemoveModule(IGNORED_PTR_ARG, handle)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, MessageBus_DecRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(handle)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_erase(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + + //Act + Gateway_LL_RemoveModule(gw, handle); + + //Assert + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_023: [ The function shall locate the MODULE_DATA object in GATEWAY_HANDLE_DATA's modules containing module and return if it cannot be found. ]*/ +TEST_FUNCTION(Gateway_LL_RemoveModule_Finds_Module_Data_Failure) +{ + //Arrange + CGatewayLLMocks mocks; + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + MODULE_HANDLE handle = Gateway_LL_AddModule(gw, (GATEWAY_PROPERTIES_ENTRY*)BASEIMPLEMENTATION::VECTOR_front(dummyProps->gateway_properties_entries)); + mocks.ResetAllCalls(); + + //Expectations + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, gw)) + .IgnoreAllArguments(); + + //Act + Gateway_LL_RemoveModule(gw, (MODULE_HANDLE)gw); + + //Assert + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +/*Tests_SRS_GATEWAY_LL_14_022: [ If GATEWAY_HANDLE_DATA's bus cannot unlink module, the function shall log the error and continue unloading the module from the GATEWAY_HANDLE. ]*/ +/*Tests_SRS_GATEWAY_LL_14_038: [ The function shall decrement the MESSAGE_BUS_HANDLE reference count. ]*/ +TEST_FUNCTION(Gateway_LL_RemoveModule_MessageBus_RemoveModule_Failure) +{ + //Arrange + CGatewayLLMocks mocks; + GATEWAY_HANDLE gw = Gateway_LL_Create(NULL); + MODULE_HANDLE handle = Gateway_LL_AddModule(gw, (GATEWAY_PROPERTIES_ENTRY*)BASEIMPLEMENTATION::VECTOR_front(dummyProps->gateway_properties_entries)); + mocks.ResetAllCalls(); + + //Expectations + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + whenShallMessageBus_RemoveModule_fail = 1; + STRICT_EXPECTED_CALL(mocks, MessageBus_RemoveModule(IGNORED_PTR_ARG, handle)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_DecRef(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_GetModuleAPIs(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mock_Module_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ModuleLoader_Unload(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_erase(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + //Act + Gateway_LL_RemoveModule(gw, handle); + + //Assert + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gw); +} + +END_TEST_SUITE(gateway_ll_unittests) diff --git a/core/tests/gateway_ll_unittests/main.c b/core/tests/gateway_ll_unittests/main.c new file mode 100644 index 00000000..387c40b3 --- /dev/null +++ b/core/tests/gateway_ll_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(gateway_ll_unittests, failedTestCount); + return failedTestCount; +} diff --git a/core/tests/gateway_unittests/CMakeLists.txt b/core/tests/gateway_unittests/CMakeLists.txt new file mode 100644 index 00000000..18ce040d --- /dev/null +++ b/core/tests/gateway_unittests/CMakeLists.txt @@ -0,0 +1,26 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#This is the CMakeLists.txt for gateway_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(testSuite gateway_unittests) +set(${testSuite}_cpp_files + ${testSuite}.cpp +) + +set(${testSuite}_c_files + ../../src/gateway.c +) + +set(${testSuite}_h_files + ../../inc/gateway.h +) + +include_directories( + ${GW_INC} + ../../parson/ +) + +build_test_artifacts(${testSuite} ON) \ No newline at end of file diff --git a/core/tests/gateway_unittests/gateway_unittests.cpp b/core/tests/gateway_unittests/gateway_unittests.cpp new file mode 100644 index 00000000..f6aa32f1 --- /dev/null +++ b/core/tests/gateway_unittests/gateway_unittests.cpp @@ -0,0 +1,843 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" +#include "azure_c_shared_utility/lock.h" + +#include "gateway.h" + +#define DUMMY_JSON_PATH "x.json" +#define MISCONFIG_JSON_PATH "invalid_json.json" +#define MISSING_INFO_JSON_PATH "missing_info_json.json" +#define VALID_JSON_PATH "valid_json.json" +#define VALID_JSON_NULL_ARGS_PATH "valid_json_null.json" + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + + + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ + +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit +#include "vector.c" + +#include /* size_t */ + +/* Types and enums */ +typedef struct json_object_t JSON_Object; +typedef struct json_array_t JSON_Array; +typedef struct json_value_t JSON_Value; + +enum json_value_type { + JSONError = -1, + JSONNull = 1, + JSONString = 2, + JSONNumber = 3, + JSONObject = 4, + JSONArray = 5, + JSONBoolean = 6 +}; +typedef int JSON_Value_Type; + +enum json_result_t { + JSONSuccess = 0, + JSONFailure = -1 +}; +typedef int JSON_Status; + +typedef void * (*JSON_Malloc_Function)(size_t); +typedef void (*JSON_Free_Function)(void *); + +/* Call only once, before calling any other function from parson API. If not called, malloc and free + from stdlib will be used for all allocations */ +void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun); + +/* Parses first JSON value in a file, returns NULL in case of error */ +JSON_Value * json_parse_file(const char *filename); + +/* Parses first JSON value in a file and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_file_with_comments(const char *filename); + +/* Parses first JSON value in a string, returns NULL in case of error */ +JSON_Value * json_parse_string(const char *string); + +/* Parses first JSON value in a string and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_string_with_comments(const char *string); + +/* Serialization */ +size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */ +JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); +JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename); +char * json_serialize_to_string(const JSON_Value *value); + +/* Pretty serialization */ +size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */ +JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); +JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename); +char * json_serialize_to_string_pretty(const JSON_Value *value); + +void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */ + +/* Comparing */ +int json_value_equals(const JSON_Value *a, const JSON_Value *b); + +/* Validation + This is *NOT* JSON Schema. It validates json by checking if object have identically + named fields with matching types. + For example schema {"name":"", "age":0} will validate + {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"}, + but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}. + In case of arrays, only first value in schema is checked against all values in tested array. + Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays, + null validates values of every type. + */ +JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value); + +/* + * JSON Object + */ +JSON_Value * json_object_get_value (const JSON_Object *object, const char *name); +const char * json_object_get_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_get_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_get_array (const JSON_Object *object, const char *name); +double json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */ +int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + +/* dotget functions enable addressing values with dot notation in nested objects, + just like in structs or c++/java/c# objects (e.g. objectA.objectB.value). + Because valid names in JSON can contain dots, some values may be inaccessible + this way. */ +JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name); +const char * json_object_dotget_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name); +double json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */ +int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + +/* Functions to get available names */ +size_t json_object_get_count(const JSON_Object *object); +const char * json_object_get_name (const JSON_Object *object, size_t index); + +/* Creates new name-value pair or frees and replaces old value with a new one. + * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value); +JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string); +JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number); +JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean); +JSON_Status json_object_set_null(JSON_Object *object, const char *name); + +/* Works like dotget functions, but creates whole hierarchy if necessary. + * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value); +JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string); +JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number); +JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean); +JSON_Status json_object_dotset_null(JSON_Object *object, const char *name); + +/* Frees and removes name-value pair */ +JSON_Status json_object_remove(JSON_Object *object, const char *name); + +/* Works like dotget function, but removes name-value pair only on exact match. */ +JSON_Status json_object_dotremove(JSON_Object *object, const char *key); + +/* Removes all name-value pairs in object */ +JSON_Status json_object_clear(JSON_Object *object); + +/* + *JSON Array + */ +JSON_Value * json_array_get_value (const JSON_Array *array, size_t index); +const char * json_array_get_string (const JSON_Array *array, size_t index); +JSON_Object * json_array_get_object (const JSON_Array *array, size_t index); +JSON_Array * json_array_get_array (const JSON_Array *array, size_t index); +double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */ +int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */ +size_t json_array_get_count (const JSON_Array *array); + +/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist. + * Order of values in array may change during execution. */ +JSON_Status json_array_remove(JSON_Array *array, size_t i); + +/* Frees and removes from array value at given index and replaces it with given one. + * Does nothing and returns JSONFailure if index doesn't exist. + * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value); +JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string); +JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number); +JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean); +JSON_Status json_array_replace_null(JSON_Array *array, size_t i); + +/* Frees and removes all values from array */ +JSON_Status json_array_clear(JSON_Array *array); + +/* Appends new value at the end of array. + * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value); +JSON_Status json_array_append_string(JSON_Array *array, const char *string); +JSON_Status json_array_append_number(JSON_Array *array, double number); +JSON_Status json_array_append_boolean(JSON_Array *array, int boolean); +JSON_Status json_array_append_null(JSON_Array *array); + +/* + *JSON Value + */ +JSON_Value * json_value_init_object (void); +JSON_Value * json_value_init_array (void); +JSON_Value * json_value_init_string (const char *string); /* copies passed string */ +JSON_Value * json_value_init_number (double number); +JSON_Value * json_value_init_boolean(int boolean); +JSON_Value * json_value_init_null (void); +JSON_Value * json_value_deep_copy (const JSON_Value *value); +void json_value_free (JSON_Value *value); + +JSON_Value_Type json_value_get_type (const JSON_Value *value); +JSON_Object * json_value_get_object (const JSON_Value *value); +JSON_Array * json_value_get_array (const JSON_Value *value); +const char * json_value_get_string (const JSON_Value *value); +double json_value_get_number (const JSON_Value *value); +int json_value_get_boolean(const JSON_Value *value); + +/* Same as above, but shorter */ +JSON_Value_Type json_type (const JSON_Value *value); +JSON_Object * json_object (const JSON_Value *value); +JSON_Array * json_array (const JSON_Value *value); +const char * json_string (const JSON_Value *value); +double json_number (const JSON_Value *value); +int json_boolean(const JSON_Value *value); + +#define parson_parson_h +#ifdef _CRT_SECURE_NO_WARNINGS +#undef _CRT_SECURE_NO_WARNINGS +#include "parson.c" +#define _CRT_SECURE_NO_WARNINGS +#else +#include "parson.c" +#endif +}; + +#undef parson_parson_h +#include "parson.h" + +TYPED_MOCK_CLASS(CGatewayMocks, CGlobalMock) +{ +public: + + /*Parson Mocks*/ + MOCK_STATIC_METHOD_1(, JSON_Value*, json_parse_file, const char *, filename) + JSON_Value* value = NULL; + if (filename != NULL) + { + value = (JSON_Value*)malloc(sizeof(BASEIMPLEMENTATION::JSON_Value)); + } + MOCK_METHOD_END(JSON_Value*, value); + + MOCK_STATIC_METHOD_1(, JSON_Object*, json_value_get_object, const JSON_Value*, value) + JSON_Object* object = NULL; + if (value != NULL) + { + object = (JSON_Object*)0x42; + } + MOCK_METHOD_END(JSON_Object*, object); + + MOCK_STATIC_METHOD_2(, JSON_Array*, json_object_get_array, const JSON_Object*, object, const char*, name) + JSON_Array* arr = NULL; + if (object != NULL && name != NULL) + { + arr = (JSON_Array*)0x42; + } + MOCK_METHOD_END(JSON_Array*, arr); + + MOCK_STATIC_METHOD_1(, size_t, json_array_get_count, const JSON_Array*, arr) + size_t size = 0; + MOCK_METHOD_END(size_t, size); + + MOCK_STATIC_METHOD_2(, JSON_Object*, json_array_get_object, const JSON_Array*, arr, size_t, index) + JSON_Object* object = NULL; + if (arr != NULL && index >= 0) + { + object = (JSON_Object*)0x42; + } + MOCK_METHOD_END(JSON_Object*, object); + + MOCK_STATIC_METHOD_2(, const char*, json_object_get_string, const JSON_Object*, object, const char*, name) + const char* string = NULL; + if (object != NULL && name != NULL) + { + string = name; + } + MOCK_METHOD_END(const char*, string); + + MOCK_STATIC_METHOD_2(, JSON_Value*, json_object_get_value, const JSON_Object*, object, const char*, name) + JSON_Value* value = NULL; + if (object != NULL && name != NULL) + { + value = (JSON_Value*)0x42; + } + MOCK_METHOD_END(JSON_Value*, value); + + MOCK_STATIC_METHOD_1(, char*, json_serialize_to_string, const JSON_Value*, value) + char* serialized_string = NULL; + const char* text = "[serialized string]"; + if (value != NULL) + { + serialized_string = (char*)malloc(sizeof(char) * strlen(text) + 1); + strcpy(serialized_string, text); + } + MOCK_METHOD_END(char*, serialized_string); + + MOCK_STATIC_METHOD_1(, void, json_value_free, JSON_Value*, value) + free(value); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_1(, void, json_free_serialized_string, char*, string) + free(string); + MOCK_VOID_METHOD_END(); + + /*Gateway Mocks*/ + MOCK_STATIC_METHOD_1(, GATEWAY_HANDLE, Gateway_LL_Create, const GATEWAY_PROPERTIES*, properties) + GATEWAY_HANDLE handle = (GATEWAY_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(1); + MOCK_METHOD_END(GATEWAY_HANDLE, handle); + + MOCK_STATIC_METHOD_1(, void, Gateway_LL_Destroy, GATEWAY_HANDLE, gw) + BASEIMPLEMENTATION::gballoc_free(gw); + MOCK_VOID_METHOD_END(); + + /*Vector Mocks*/ + MOCK_STATIC_METHOD_1(, VECTOR_HANDLE, VECTOR_create, size_t, elementSize) + VECTOR_HANDLE vector = BASEIMPLEMENTATION::VECTOR_create(elementSize); + MOCK_METHOD_END(VECTOR_HANDLE, vector); + + MOCK_STATIC_METHOD_1(, void, VECTOR_destroy, VECTOR_HANDLE, handle) + BASEIMPLEMENTATION::VECTOR_destroy(handle); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_3(, int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements) + int result1 = BASEIMPLEMENTATION::VECTOR_push_back(handle, elements, numElements); + MOCK_METHOD_END(int, result1); + + MOCK_STATIC_METHOD_3(, void, VECTOR_erase, VECTOR_HANDLE, handle, void*, elements, size_t, index) + BASEIMPLEMENTATION::VECTOR_erase(handle, elements, index); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_2(, void*, VECTOR_element, const VECTOR_HANDLE, handle, size_t, index) + auto element = BASEIMPLEMENTATION::VECTOR_element(handle, index); + MOCK_METHOD_END(void*, element); + + MOCK_STATIC_METHOD_1(, void*, VECTOR_front, const VECTOR_HANDLE, handle) + auto element = BASEIMPLEMENTATION::VECTOR_front(handle); + MOCK_METHOD_END(void*, element); + + MOCK_STATIC_METHOD_1(, size_t, VECTOR_size, const VECTOR_HANDLE, handle) + auto size = BASEIMPLEMENTATION::VECTOR_size(handle); + MOCK_METHOD_END(size_t, size); + + MOCK_STATIC_METHOD_3(, void*, VECTOR_find_if, const VECTOR_HANDLE, handle, PREDICATE_FUNCTION, pred, const void*, value) + void* element = BASEIMPLEMENTATION::VECTOR_find_if(handle, pred, value); + MOCK_METHOD_END(void*, element); + + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_2(, void*, gballoc_realloc, void*, ptr, size_t, size) + MOCK_METHOD_END(void*, BASEIMPLEMENTATION::gballoc_realloc(ptr, size)); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , JSON_Value*, json_parse_file, const char *, filename); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , JSON_Object*, json_value_get_object, const JSON_Value*, value); +DECLARE_GLOBAL_MOCK_METHOD_2(CGatewayMocks, , JSON_Array*, json_object_get_array, const JSON_Object*, object, const char*, name); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , size_t, json_array_get_count, const JSON_Array*, arr); +DECLARE_GLOBAL_MOCK_METHOD_2(CGatewayMocks, , JSON_Object*, json_array_get_object, const JSON_Array*, arr, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_2(CGatewayMocks, , const char*, json_object_get_string, const JSON_Object*, object, const char*, name); +DECLARE_GLOBAL_MOCK_METHOD_2(CGatewayMocks, , JSON_Value*, json_object_get_value, const JSON_Object*, object, const char*, name); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , char*, json_serialize_to_string, const JSON_Value*, value); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , void, json_value_free, JSON_Value*, value); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , void, json_free_serialized_string, char*, string); + +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , GATEWAY_HANDLE, Gateway_LL_Create, const GATEWAY_PROPERTIES*, properties); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , void, Gateway_LL_Destroy, GATEWAY_HANDLE, gw); + +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , VECTOR_HANDLE, VECTOR_create, size_t, elementSize); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , void, VECTOR_destroy, VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CGatewayMocks, , int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements); +DECLARE_GLOBAL_MOCK_METHOD_3(CGatewayMocks, , void, VECTOR_erase, VECTOR_HANDLE, handle, void*, elements, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_2(CGatewayMocks, , void*, VECTOR_element, const VECTOR_HANDLE, handle, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , void*, VECTOR_front, const VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , size_t, VECTOR_size, const VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CGatewayMocks, , void*, VECTOR_find_if, const VECTOR_HANDLE, handle, PREDICATE_FUNCTION, pred, const void*, value); + + +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_2(CGatewayMocks, , void*, gballoc_realloc, void*, ptr, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CGatewayMocks, , void, gballoc_free, void*, ptr) + +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; +static MICROMOCK_MUTEX_HANDLE g_testByTest; + + +BEGIN_TEST_SUITE(gateway_unittests) + +TEST_SUITE_INITIALIZE(TestClassInitialize) +{ + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); +} + +TEST_SUITE_CLEANUP(TestClassCleanup) +{ + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); +} + +TEST_FUNCTION_INITIALIZE(TestMethodInitialize) +{ + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } +} + +TEST_FUNCTION_CLEANUP(TestMethodCleanup) +{ + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } +} + +/*Tests_SRS_GATEWAY_14_001: [If file_path is NULL the function shall return NULL.]*/ +TEST_FUNCTION(Gateway_Create_Returns_NULL_For_NULL_JSON_Input) +{ + //Act + GATEWAY_HANDLE gateway = Gateway_Create_From_JSON(NULL); + + //Assert + ASSERT_IS_NULL(gateway); +} + +/*Tests_SRS_GATEWAY_14_003: [The function shall return NULL if the file contents could not be read and / or parsed to a JSON_Value.]*/ +TEST_FUNCTION(Gateway_Create_Returns_NULL_If_File_Not_Exist) +{ + //Arrange + CGatewayMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_file(DUMMY_JSON_PATH)) + .SetFailReturn((JSON_Value*)NULL); + + //Act + GATEWAY_HANDLE gateway = Gateway_Create_From_JSON(DUMMY_JSON_PATH); + + //Assert + ASSERT_IS_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); +} + +/*Tests_SRS_GATEWAY_14_008: [ This function shall return NULL upon any memory allocation failure. ]*/ +TEST_FUNCTION(Gateway_Create_Returns_NULL_On_Properties_Malloc_Failure) +{ + //Arrange + CGatewayMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_file(DUMMY_JSON_PATH)); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(sizeof(GATEWAY_PROPERTIES))) + .SetFailReturn((void*)NULL); + + //Act + GATEWAY_HANDLE gateway = Gateway_Create_From_JSON(DUMMY_JSON_PATH); + + //Assert + ASSERT_IS_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); + +} + +/*Tests_SRS_GATEWAY_14_002: [The function shall use parson to read the file and parse the JSON string to a parson JSON_Value structure.]*/ +/*Tests_SRS_GATEWAY_14_005: [The function shall set the value of const void* module_properties in the GATEWAY_PROPERTIES instance to a char* representing the serialized args value for the particular module.]*/ +/*Tests_SRS_GATEWAY_14_007: [The function shall use the GATEWAY_PROPERTIES instance to create and return a GATEWAY_HANDLE using the lower level API.]*/ +TEST_FUNCTION(Gateway_Create_Parses_Valid_JSON_Configuration_File) +{ + //Arrange + CGatewayMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_file(VALID_JSON_PATH)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(sizeof(GATEWAY_PROPERTIES))); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "modules")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn(2); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(GATEWAY_PROPERTIES_ENTRY))); + + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module name")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module path")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_value(IGNORED_PTR_ARG, "args")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_serialize_to_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module name")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module path")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_value(IGNORED_PTR_ARG, "args")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_serialize_to_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Gateway_LL_Create(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_free_serialized_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_free_serialized_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + GATEWAY_HANDLE gateway = Gateway_Create_From_JSON(VALID_JSON_PATH); + + //Assert + ASSERT_IS_NOT_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gateway); +} + +/*Tests_SRS_GATEWAY_14_002: [The function shall use parson to read the file and parse the JSON string to a parson JSON_Value structure.]*/ +/*Tests_SRS_GATEWAY_14_004: [The function shall traverse the JSON_Value object to initialize a GATEWAY_PROPERTIES instance.]*/ +/*Tests_SRS_GATEWAY_14_005: [The function shall set the value of const void* module_properties in the GATEWAY_PROPERTIES instance to a char* representing the serialized args value for the particular module.]*/ +/*Tests_SRS_GATEWAY_14_007: [The function shall use the GATEWAY_PROPERTIES instance to create and return a GATEWAY_HANDLE using the lower level API.]*/ +TEST_FUNCTION(Gateway_Create_Traverses_JSON_Value_Success) +{ + //Arrange + CGatewayMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_file(VALID_JSON_PATH)); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(sizeof(GATEWAY_PROPERTIES))); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "modules")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn(2); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(GATEWAY_PROPERTIES_ENTRY))); + + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module name")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module path")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_value(IGNORED_PTR_ARG, "args")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_serialize_to_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module name")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module path")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_value(IGNORED_PTR_ARG, "args")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_serialize_to_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Gateway_LL_Create(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_free_serialized_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_free_serialized_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + GATEWAY_HANDLE gateway = Gateway_Create_From_JSON(VALID_JSON_PATH); + + //Assert + ASSERT_IS_NOT_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gateway); +} + +/*Tests_SRS_GATEWAY_14_002: [The function shall use parson to read the file and parse the JSON string to a parson JSON_Value structure.]*/ +/*Tests_SRS_GATEWAY_14_004: [The function shall traverse the JSON_Value object to initialize a GATEWAY_PROPERTIES instance.]*/ +/*Tests_SRS_GATEWAY_14_005: [The function shall set the value of const void* module_properties in the GATEWAY_PROPERTIES instance to a char* representing the serialized args value for the particular module.]*/ +/*Tests_SRS_GATEWAY_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ +TEST_FUNCTION(Gateway_Create_Traverses_JSON_Push_Back_Fail) +{ + //Arrange + CGatewayMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_file(VALID_JSON_PATH)); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(sizeof(GATEWAY_PROPERTIES))); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "modules")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn(2); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(GATEWAY_PROPERTIES_ENTRY))); + + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module name")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module path")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_value(IGNORED_PTR_ARG, "args")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_serialize_to_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module name")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module path")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_value(IGNORED_PTR_ARG, "args")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_serialize_to_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn(-1); + + STRICT_EXPECTED_CALL(mocks, json_free_serialized_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_free_serialized_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + GATEWAY_HANDLE gateway = Gateway_Create_From_JSON(VALID_JSON_PATH); + + //Assert + ASSERT_IS_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); + +} + +/*Tests_SRS_GATEWAY_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ +TEST_FUNCTION(Gateway_Create_Traverses_JSON_Value_NULL_Array) +{ + //Arrange + CGatewayMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_file(VALID_JSON_PATH)); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(sizeof(GATEWAY_PROPERTIES))); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "modules")) + .IgnoreArgument(1) + .SetFailReturn((JSON_Array*)NULL); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + GATEWAY_HANDLE gateway = Gateway_Create_From_JSON(VALID_JSON_PATH); + + //Assert + ASSERT_IS_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); +} + +/*Tests_SRS_GATEWAY_14_008: [This function shall return NULL upon any memory allocation failure.]*/ +TEST_FUNCTION(Gateway_Create_Traverses_JSON_Value_VECTOR_Create_Fail) +{ + //Arrange + CGatewayMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_file(VALID_JSON_PATH)); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(sizeof(GATEWAY_PROPERTIES))); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "modules")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(GATEWAY_PROPERTIES_ENTRY))) + .SetFailReturn((VECTOR_HANDLE)NULL); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + GATEWAY_HANDLE gateway = Gateway_Create_From_JSON(VALID_JSON_PATH); + + //Assert + ASSERT_IS_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); +} + +/*Tests_SRS_GATEWAY_14_004: [The function shall traverse the JSON_Value object to initialize a GATEWAY_PROPERTIES instance.]*/ +/*Tests_SRS_GATEWAY_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ +TEST_FUNCTION(Gateway_Create_Traverses_JSON_Value_NULL_Root_Value_Failure) +{ + //Arrange + CGatewayMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_file(DUMMY_JSON_PATH)); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(sizeof(GATEWAY_PROPERTIES))); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((JSON_Object*)NULL); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + GATEWAY_HANDLE gateway = Gateway_Create_From_JSON(DUMMY_JSON_PATH); + + //Assert + ASSERT_IS_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); + + //Cleanup + Gateway_LL_Destroy(gateway); +} + +/*Tests_SRS_GATEWAY_14_006: [The function shall return NULL if the JSON_Value contains incomplete information.]*/ +TEST_FUNCTION(Gateway_Create_Fails_For_Missing_Info_In_JSON_Configuration) +{ + //Arrange + CGatewayMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_file(MISSING_INFO_JSON_PATH)); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(sizeof(GATEWAY_PROPERTIES))); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "modules")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn(2); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(GATEWAY_PROPERTIES_ENTRY))); + + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module name")) + .IgnoreArgument(1) + .SetFailReturn((char*)NULL); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "module path")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + //Act + GATEWAY_HANDLE gateway = Gateway_Create_From_JSON(MISSING_INFO_JSON_PATH); + + //Assert + ASSERT_IS_NULL(gateway); + mocks.AssertActualAndExpectedCalls(); +} + +END_TEST_SUITE(gateway_unittests) diff --git a/core/tests/gateway_unittests/main.c b/core/tests/gateway_unittests/main.c new file mode 100644 index 00000000..11b716b2 --- /dev/null +++ b/core/tests/gateway_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(gateway_unittests, failedTestCount); + return failedTestCount; +} diff --git a/core/tests/gw_e2etests/CMakeLists.txt b/core/tests/gw_e2etests/CMakeLists.txt new file mode 100644 index 00000000..d874f291 --- /dev/null +++ b/core/tests/gw_e2etests/CMakeLists.txt @@ -0,0 +1,64 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for the gw e2e tests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName gw_e2etests) +set(${theseTestsName}_cpp_files +${theseTestsName}.cpp +) + +#setting the dynamic_loader file based on OS that it is used +if(WIN32) + set(modules_c_file ./module_config_windows.c) +elseif(LINUX) + set(modules_c_file ./module_config_linux.c) +endif() + +set(${theseTestsName}_c_files + ${modules_c_file} +) + +include_directories(../../../samples/protocol_encapsulation/inc ${IOTHUB_CLIENT_INC_FOLDER} e2e_module/inc/) +include_directories(../../../deps/azure-iot-sdks/c/testtools/iothub_test/inc) +include_directories(../../../modules/common) +include_directories(../../../modules/iothubhttp/inc) +include_directories(../../../modules/identitymap/inc) +include_directories(${GW_INC}) + +build_test_artifacts(${theseTestsName} ON) + +if(WIN32) + if(TARGET ${theseTestsName}_dll) + target_link_libraries(${theseTestsName}_dll + gateway + iothub_test + rpcrt4 + ) + endif() + + if(TARGET ${theseTestsName}_exe) + target_link_libraries(${theseTestsName}_exe + gateway + iothub_test + rpcrt4 + ) + endif() +else() + if(TARGET ${theseTestsName}_exe) + target_link_libraries(${theseTestsName}_exe + iothub_test + gateway + aziotsharedutil + uuid + ) + target_link_libraries(${theseTestsName}_exe pthread) + linkUAMQP(${theseTestsName}_exe) + linkHttp(${theseTestsName}_exe) + endif() + +endif() + +add_subdirectory(e2e_module) \ No newline at end of file diff --git a/core/tests/gw_e2etests/doc/E2ETestDiagram.vsdx b/core/tests/gw_e2etests/doc/E2ETestDiagram.vsdx new file mode 100644 index 00000000..da23ea60 Binary files /dev/null and b/core/tests/gw_e2etests/doc/E2ETestDiagram.vsdx differ diff --git a/core/tests/gw_e2etests/e2e_module/CMakeLists.txt b/core/tests/gw_e2etests/e2e_module/CMakeLists.txt new file mode 100644 index 00000000..8a0862cc --- /dev/null +++ b/core/tests/gw_e2etests/e2e_module/CMakeLists.txt @@ -0,0 +1,22 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for the e2e module +cmake_minimum_required(VERSION 2.8.11) + +set(e2e_module_sources + ./src/e2e_module.c +) + +set(e2e_module_headers + ./inc/e2e_module.h +) + + +include_directories(./inc) +include_directories(${GW_INC}) + +add_library(e2e_module MODULE ${e2e_module_sources} ${e2e_module_headers}) + +target_link_libraries(e2e_module gateway) +linkSharedUtil(e2e_module) diff --git a/core/tests/gw_e2etests/e2e_module/inc/e2e_module.h b/core/tests/gw_e2etests/e2e_module/inc/e2e_module.h new file mode 100644 index 00000000..8789ffd4 --- /dev/null +++ b/core/tests/gw_e2etests/e2e_module/inc/e2e_module.h @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef E2EMODULE_H +#define E2EMODULE_H + +/*including module.h dictates that MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void); is implemented by the module*/ +#include "module.h" + +typedef struct E2EMODULE_CONFIG +{ + const char* macAddress; + const char* sendData; +}E2EMODULE_CONFIG; + +#endif /*E2EMODULE_H*/ diff --git a/core/tests/gw_e2etests/e2e_module/src/e2e_module.c b/core/tests/gw_e2etests/e2e_module/src/e2e_module.c new file mode 100644 index 00000000..60072779 --- /dev/null +++ b/core/tests/gw_e2etests/e2e_module/src/e2e_module.c @@ -0,0 +1,190 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "e2e_module.h" +#include "azure_c_shared_utility/threadapi.h" +#include "message.h" +#include "messageproperties.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/crt_abstractions.h" + +#include "module.h" +#include "message_bus.h" + +#define GW_E2E_MODULE "E2EMODULE" + +typedef struct E2E_MODULE_DATA_TAG +{ + MESSAGE_BUS_HANDLE bus; + THREAD_HANDLE e2eModulethread; + char* fakeMacAddress; + char* dataToSend; +} E2E_MODULE_DATA; + + +static void E2EModule_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + (void)moduleHandle; + (void)messageHandle; + return; +} + +static void E2EModule_Destroy(MODULE_HANDLE moduleHandle) +{ + if (moduleHandle == NULL) + { + LogError("Attempt to destroy NULL module"); + } + else + { + E2E_MODULE_DATA* module_data = (E2E_MODULE_DATA*)moduleHandle; + int result; + + /* join the thread */ + if (ThreadAPI_Join(module_data->e2eModulethread, &result) != THREADAPI_OK) + { + LogError("Attempt to ThreadAPI_Join Failed."); + } + + free((void*)module_data->fakeMacAddress); + free((void*)module_data->dataToSend); + free(module_data); + } +} + +static int e2e_module_worker(void * user_data) +{ + E2E_MODULE_DATA* module_data = (E2E_MODULE_DATA*)user_data; + + MESSAGE_CONFIG newMessageCfg; + MAP_HANDLE newProperties = Map_Create(NULL); + + if (newProperties == NULL) + { + LogError("Failed to create message properties"); + } + else + { + if (Map_Add(newProperties, GW_SOURCE_PROPERTY, GW_E2E_MODULE) != MAP_OK) + { + LogError("Failed to set source property"); + } + else if (Map_Add(newProperties, GW_MAC_ADDRESS_PROPERTY, module_data->fakeMacAddress) != MAP_OK) + { + LogError("Failed to set macAddress property"); + } + else + { + MESSAGE_HANDLE newMessage; + + newMessageCfg.sourceProperties = newProperties; + newMessageCfg.size = strlen(module_data->dataToSend); + newMessageCfg.source = (const unsigned char*)module_data->dataToSend; + + newMessage = Message_Create(&newMessageCfg); + if (newMessage == NULL) + { + LogError("Failed to create new message"); + } + else + { + if (MessageBus_Publish(module_data->bus, newMessage) != MESSAGE_BUS_OK) + { + LogError("Failed to publish module data to the message bus."); + } + Message_Destroy(newMessage); + } + } + Map_Destroy(newProperties); + } + + return 0; +} + + +static MODULE_HANDLE E2EModule_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + E2E_MODULE_DATA * result; + if (busHandle == NULL || configuration == NULL) + { + LogError("invalid Fake E2E module args."); + result = NULL; + } + else + { + /* allocate module data struct */ + result = (E2E_MODULE_DATA*)malloc(sizeof(E2E_MODULE_DATA)); + if (result == NULL) + { + LogError("couldn't allocate memory for E2E Module"); + } + else + { + E2EMODULE_CONFIG* e2eModuleConfig = (E2EMODULE_CONFIG*)configuration; + int status; + + /* save the message bus */ + result->bus = busHandle; + + /* save fake MacAddress */ + status = mallocAndStrcpy_s(&(result->fakeMacAddress), e2eModuleConfig->macAddress); + if (status != 0) + { + LogError("MacAddress did not copy"); + free(result); + result = NULL; + } + else + { + status = mallocAndStrcpy_s(&(result->dataToSend), e2eModuleConfig->sendData); + if (status != 0) + { + LogError("MacAddress did not copy"); + free(result->fakeMacAddress); + free(result); + result = NULL; + } + else + { + /* Create a fake data thread. */ + if (ThreadAPI_Create( + &(result->e2eModulethread), + e2e_module_worker, + (void*)result) != THREADAPI_OK) + { + LogError("ThreadAPI_Create failed"); + free(result->dataToSend); + free(result->fakeMacAddress); + free(result); + result = NULL; + } + else + { + /* Thread started, module created, all complete.*/ + } + } + } + } + } + return result; +} + +/* + * Required for all modules: the public API and the designated implementation functions. + */ +static const MODULE_APIS E2EModule_APIS_all = +{ + E2EModule_Create, + E2EModule_Destroy, + E2EModule_Receive +}; + +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +{ + return &E2EModule_APIS_all; +} diff --git a/core/tests/gw_e2etests/gw_e2etests.cpp b/core/tests/gw_e2etests/gw_e2etests.cpp new file mode 100644 index 00000000..fda1b9ae --- /dev/null +++ b/core/tests/gw_e2etests/gw_e2etests.cpp @@ -0,0 +1,235 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "iothubtest.h" +#include "iothub_account.h" +#include "azure_c_shared_utility/platform.h" + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/lock.h" +#include "gateway_ll.h" +#include "iothubhttp.h" +#include "e2e_module.h" +#include "messageproperties.h" +#include "identitymap.h" +#include "azure_c_shared_utility/threadapi.h" +#include "module_config_resources.h" + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +static size_t g_iotHubTestId = 0; +static IOTHUB_ACCOUNT_INFO_HANDLE g_iothubAcctInfo = NULL; + +#define MAX_CLOUD_TRAVEL_TIME 60 +#define SEND_DATA_LENGTH 50 +const char* TEST_EVENT_DATA_FMT = "{\"data\":\"%.24s\",\"id\":\"%d\"}"; + +typedef struct EXPECTED_SEND_DATA_TAG +{ + const char* expectedString; + bool wasFound; +} EXPECTED_SEND_DATA; + +static EXPECTED_SEND_DATA* EventData_Create(void) +{ + EXPECTED_SEND_DATA* result = (EXPECTED_SEND_DATA*)malloc(sizeof(EXPECTED_SEND_DATA)); + if (result != NULL) + { + char* temp = (char*)malloc(SEND_DATA_LENGTH); + + if (temp == NULL) + { + free(result); + result = NULL; + } + else + { + char* tempString; + time_t t = time(NULL); + (void)sprintf_s(temp, SEND_DATA_LENGTH, TEST_EVENT_DATA_FMT, ctime(&t), g_iotHubTestId); + if ((tempString = (char*)malloc(strlen(temp) + 1)) == NULL) + { + free(result); + result = NULL; + } + else + { + strcpy(tempString, temp); + result->expectedString = tempString; + result->wasFound = false; + free(temp); + } + } + } + return result; +} + + +static void EventData_Destroy(EXPECTED_SEND_DATA* data) +{ + + if (data != NULL) + { + free((char*)data->expectedString); + free(data); + } +} +static int IoTHubCallback(void* context, const char* data, size_t size) +{ + int result = 0; // 0 means "keep processing" + + EXPECTED_SEND_DATA* expectedData = (EXPECTED_SEND_DATA*)context; + if (expectedData != NULL) + { + if ( + (strlen(expectedData->expectedString) == size) && + (memcmp(expectedData->expectedString, data, size) == 0) + ) + { + expectedData->wasFound = true; + result = 1; + } + } + return result; +} + +BEGIN_TEST_SUITE(gw_e2etests) + + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + platform_init(); + g_iothubAcctInfo = IoTHubAccount_Init(true, "gw_e2e_tests"); + ASSERT_IS_NOT_NULL(g_iothubAcctInfo); + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + IoTHubAccount_deinit(g_iothubAcctInfo); + platform_deinit(); + + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + g_iotHubTestId++; + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + } + + TEST_FUNCTION(GW_e2eModule_Sending_Data) + { + ///arrange + GATEWAY_HANDLE e2eGatewayInstance; + + /* Setup: data for IoT Hub Module */ + IOTHUBHTTP_CONFIG iotHubConfig; + + iotHubConfig.IoTHubName = IoTHubAccount_GetIoTHubName(g_iothubAcctInfo); + iotHubConfig.IoTHubSuffix = IoTHubAccount_GetIoTHubSuffix(g_iothubAcctInfo); + + + E2EMODULE_CONFIG e2eModuleConfiguration; + EXPECTED_SEND_DATA* sendData = EventData_Create(); + ASSERT_IS_NOT_NULL_WITH_MSG(sendData, "Failure creating data to be sent"); + + e2eModuleConfiguration.macAddress = "01:01:01:01:01:01"; + e2eModuleConfiguration.sendData = sendData->expectedString; + + /* Setup: data for identity map module */ + IDENTITY_MAP_CONFIG e2eModuleMapping[1]; + e2eModuleMapping[0].deviceId = IoTHubAccount_GetDeviceId(g_iothubAcctInfo); + e2eModuleMapping[0].deviceKey = IoTHubAccount_GetDeviceKey(g_iothubAcctInfo); + e2eModuleMapping[0].macAddress = "01:01:01:01:01:01"; + + VECTOR_HANDLE e2eModuleMappingVector = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + if (e2eModuleMappingVector == NULL) + { + ASSERT_FAIL("Could not create vector for identity map configuration."); + } + else + { + if (VECTOR_push_back(e2eModuleMappingVector, &e2eModuleMapping, 1) != 0) + { + ASSERT_FAIL("Could not push data into vector for identity map configuration."); + } + } + + GATEWAY_PROPERTIES_ENTRY modules[3]; + + modules[0].module_configuration = &iotHubConfig; + modules[0].module_name = "IoTHub"; + modules[0].module_path = iothubhttp_module_path(); + + + modules[1].module_configuration = e2eModuleMappingVector; + modules[1].module_name = GW_IDMAP_MODULE; + modules[1].module_path = identity_map_module_path(); + + modules[2].module_configuration = &e2eModuleConfiguration; + modules[2].module_name = "E2ETest"; + modules[2].module_path = e2e_module_path(); + + + GATEWAY_PROPERTIES m6GatewayProperties; + VECTOR_HANDLE gatewayProps = VECTOR_create(sizeof(GATEWAY_PROPERTIES_ENTRY)); + + VECTOR_push_back(gatewayProps, &modules, 3); + + + ///act + m6GatewayProperties.gateway_properties_entries = gatewayProps; + e2eGatewayInstance = Gateway_LL_Create(&m6GatewayProperties); + + + + ///assert + ASSERT_IS_NOT_NULL(e2eGatewayInstance); + + ThreadAPI_Sleep(MAX_CLOUD_TRAVEL_TIME * 1000); + + Gateway_LL_Destroy(e2eGatewayInstance); + + VECTOR_destroy(e2eModuleMappingVector); + VECTOR_destroy(gatewayProps); + + platform_deinit(); + platform_init(); + //step3: get the data from the other side + { + IOTHUB_TEST_HANDLE iotHubTestHandle = IoTHubTest_Initialize(IoTHubAccount_GetEventHubConnectionString(g_iothubAcctInfo), IoTHubAccount_GetIoTHubConnString(g_iothubAcctInfo), IoTHubAccount_GetDeviceId(g_iothubAcctInfo), IoTHubAccount_GetDeviceKey(g_iothubAcctInfo), IoTHubAccount_GetEventhubListenName(g_iothubAcctInfo), IoTHubAccount_GetEventhubAccessKey(g_iothubAcctInfo), IoTHubAccount_GetSharedAccessSignature(g_iothubAcctInfo), IoTHubAccount_GetEventhubConsumerGroup(g_iothubAcctInfo)); + ASSERT_IS_NOT_NULL(iotHubTestHandle); + + IOTHUB_TEST_CLIENT_RESULT result = IoTHubTest_ListenForEventForMaxDrainTime(iotHubTestHandle, IoTHubCallback, IoTHubAccount_GetIoTHubPartitionCount(g_iothubAcctInfo), sendData); + IoTHubTest_Deinit(iotHubTestHandle); + } + + ASSERT_IS_TRUE_WITH_MSG(sendData->wasFound, "Failure receiving data from eventhub"); + ///cleanup + + EventData_Destroy(sendData); + } +END_TEST_SUITE(gw_e2etests) diff --git a/core/tests/gw_e2etests/main.c b/core/tests/gw_e2etests/main.c new file mode 100644 index 00000000..4c90a10f --- /dev/null +++ b/core/tests/gw_e2etests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(gw_e2etests, failedTestCount); + return failedTestCount; +} diff --git a/core/tests/gw_e2etests/module_config_linux.c b/core/tests/gw_e2etests/module_config_linux.c new file mode 100644 index 00000000..343c0174 --- /dev/null +++ b/core/tests/gw_e2etests/module_config_linux.c @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "gateway_ll.h" + +const char* e2e_module_path_string = "e2e_module/libe2e_module.so"; +const char* iothubhttp_path_string = "../../../modules/iothubhttp/libiothubhttp.so";; +const char* identity_map_path_string = "../../../modules/identitymap/libidentity_map.so"; + +const char* e2e_module_path() +{ + return e2e_module_path_string; +} + +const char* iothubhttp_module_path() +{ + return iothubhttp_path_string; +} + +const char* identity_map_module_path() +{ + return identity_map_path_string; +} \ No newline at end of file diff --git a/core/tests/gw_e2etests/module_config_windows.c b/core/tests/gw_e2etests/module_config_windows.c new file mode 100644 index 00000000..dbf0e524 --- /dev/null +++ b/core/tests/gw_e2etests/module_config_windows.c @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "gateway_ll.h" + +const char* e2e_module_path_string = "..\\e2e_module\\Debug\\e2e_module.dll"; +const char* iothubhttp_path_string = "..\\..\\..\\..\\modules\\iothubhttp\\Debug\\iothubhttp.dll"; +const char* identity_map_path_string = "..\\..\\..\\..\\modules\\identitymap\\Debug\\identity_map.dll"; + +const char* e2e_module_path() +{ + return e2e_module_path_string; +} + +const char* iothubhttp_module_path() +{ + return iothubhttp_path_string; +} + +const char* identity_map_module_path() +{ + return identity_map_path_string; +} \ No newline at end of file diff --git a/core/tests/gwmessage_unittests/CMakeLists.txt b/core/tests/gwmessage_unittests/CMakeLists.txt new file mode 100644 index 00000000..ee9bdc8f --- /dev/null +++ b/core/tests/gwmessage_unittests/CMakeLists.txt @@ -0,0 +1,22 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for gwmessage_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName gwmessage_unittests) +set(${theseTestsName}_cpp_files +${theseTestsName}.cpp +) + +set(${theseTestsName}_c_files + ../../src/message.c +) + +set(${theseTestsName}_h_files +) + +include_directories(${GW_INC}) + +build_test_artifacts(${theseTestsName} ON) \ No newline at end of file diff --git a/core/tests/gwmessage_unittests/gwmessage_unittests.cpp b/core/tests/gwmessage_unittests/gwmessage_unittests.cpp new file mode 100644 index 00000000..9dd38734 --- /dev/null +++ b/core/tests/gwmessage_unittests/gwmessage_unittests.cpp @@ -0,0 +1,1019 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/lock.h" + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit + +}; + +#include "message.h" + +static size_t currentmalloc_call; +static size_t whenShallmalloc_fail; + +static size_t currentConstMap_Create_call; +static size_t whenShallConstMap_Create_fail; + +static size_t currentConstMap_Clone_call; +static size_t whenShallConstMap_Clone_fail; + +static size_t currentCONSTBUFFER_Create_call; +static size_t whenShallCONSTBUFFER_Create_fail; +static size_t currentCONSTBUFFER_refCount; + +static size_t currentCONSTBUFFER_Clone_call; +static size_t whenShallCONSTBUFFER_Clone_fail; + +TYPED_MOCK_CLASS(CMessageMocks, CGlobalMock) +{ +public: + + // memory + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2; + currentmalloc_call++; + if (whenShallmalloc_fail>0) + { + if (currentmalloc_call == whenShallmalloc_fail) + { + result2 = NULL; + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + } + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + } + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + // ConstMap mocks + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, ConstMap_Create, MAP_HANDLE, sourceMap) + CONSTMAP_HANDLE result2; + currentConstMap_Create_call ++; + if (whenShallConstMap_Create_fail == currentConstMap_Create_call) + { + result2 = NULL; + } + else + { + result2 = (CONSTMAP_HANDLE)malloc(1); + *(unsigned char*)result2 = 1; + } + MOCK_METHOD_END(CONSTMAP_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, ConstMap_Clone, CONSTMAP_HANDLE, handle) + CONSTMAP_HANDLE result3; + currentConstMap_Clone_call++; + if (whenShallConstMap_Clone_fail == currentConstMap_Clone_call) + { + result3 = NULL; + } + else + { + result3 = handle; + *(unsigned char*)result3 += 1; + } + MOCK_METHOD_END(CONSTMAP_HANDLE, result3) + + MOCK_STATIC_METHOD_1(, void, ConstMap_Destroy, CONSTMAP_HANDLE, map) + unsigned char refCount = --(*(unsigned char*)map); + if (refCount == 0) + free(map); + MOCK_VOID_METHOD_END() + + // CONSTBUFFER mocks. + MOCK_STATIC_METHOD_2(, CONSTBUFFER_HANDLE, CONSTBUFFER_Create, const unsigned char*, source, size_t, size) + CONSTBUFFER_HANDLE result1; + currentCONSTBUFFER_Create_call++; + if (whenShallCONSTBUFFER_Create_fail == currentCONSTBUFFER_Create_call) + { + result1 = NULL; + } + else + { + result1 = (CONSTBUFFER_HANDLE)malloc(sizeof(CONSTBUFFER)); + (*(CONSTBUFFER*)result1).size = size; + if (size == 0) + { + (*(CONSTBUFFER*)result1).buffer = NULL; + } + else + { + unsigned char* temp = (unsigned char*)malloc(size); + memcpy(temp, source, size); + (*(CONSTBUFFER*)result1).buffer = temp; + } + currentCONSTBUFFER_refCount = 1; + } + MOCK_METHOD_END(CONSTBUFFER_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, CONSTBUFFER_HANDLE, CONSTBUFFER_Clone, CONSTBUFFER_HANDLE, constbufferHandle) + CONSTBUFFER_HANDLE result2; + currentCONSTBUFFER_Clone_call++; + if (currentCONSTBUFFER_Clone_call == whenShallCONSTBUFFER_Clone_fail) + { + result2 = NULL; + } + else + { + result2 = constbufferHandle; + currentCONSTBUFFER_refCount++; + } + MOCK_METHOD_END(CONSTBUFFER_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, const CONSTBUFFER*, CONSTBUFFER_GetContent, CONSTBUFFER_HANDLE, constbufferHandle) + CONSTBUFFER* result3 = (CONSTBUFFER*)constbufferHandle; + MOCK_METHOD_END(const CONSTBUFFER*, result3) + + MOCK_STATIC_METHOD_1(, void, CONSTBUFFER_Destroy, CONSTBUFFER_HANDLE, constbufferHandle) + --currentCONSTBUFFER_refCount; + if (currentCONSTBUFFER_refCount == 0) + { + CONSTBUFFER * fakeBuffer = (CONSTBUFFER*)constbufferHandle; + if (fakeBuffer->buffer != NULL) + { + free((void*)fakeBuffer->buffer); + } + free(fakeBuffer); + } + MOCK_VOID_METHOD_END() + +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageMocks, , void, gballoc_free, void*, ptr); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageMocks, , CONSTMAP_HANDLE, ConstMap_Create, MAP_HANDLE, sourceMap); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageMocks, , CONSTMAP_HANDLE, ConstMap_Clone, CONSTMAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageMocks, , void, ConstMap_Destroy, CONSTMAP_HANDLE, map); +DECLARE_GLOBAL_MOCK_METHOD_2(CMessageMocks, , CONSTBUFFER_HANDLE, CONSTBUFFER_Create, const unsigned char*, source, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageMocks, , CONSTBUFFER_HANDLE, CONSTBUFFER_Clone, CONSTBUFFER_HANDLE, constbufferHandle); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageMocks, , const CONSTBUFFER*, CONSTBUFFER_GetContent, CONSTBUFFER_HANDLE, constbufferHandle); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageMocks, , void, CONSTBUFFER_Destroy, CONSTBUFFER_HANDLE, constbufferHandle); + +BEGIN_TEST_SUITE(gwmessage_unittests) + + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + + currentmalloc_call = 0; + whenShallmalloc_fail = 0; + + currentConstMap_Create_call = 0; + whenShallConstMap_Create_fail = 0; + currentConstMap_Clone_call = 0; + whenShallConstMap_Clone_fail = 0; + currentCONSTBUFFER_Create_call = 0; + whenShallCONSTBUFFER_Create_fail = 0; + currentCONSTBUFFER_refCount = 0; + currentCONSTBUFFER_Clone_call = 0; + whenShallCONSTBUFFER_Clone_fail = 0; + + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + } + + /*Tests_SRS_MESSAGE_02_002: [If cfg is NULL then Message_Create shall return NULL.]*/ + TEST_FUNCTION(Message_Create_with_NULL_parameter_fails) + { + ///arrange + CMessageMocks mocks; + + ///act + auto r = Message_Create(NULL); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_02_003: [If field source of cfg is NULL and size is not zero, then Message_Create shall fail and return NULL.]*/ + TEST_FUNCTION(Message_Create_with_NULL_source_and_non_zero_size_fails) + { + ///arrange + CMessageMocks mocks; + MESSAGE_CONFIG c = {1, NULL, (MAP_HANDLE)&mocks}; + + ///act + auto r = Message_Create(&c); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_02_006: [Otherwise, Message_Create shall return a non-NULL handle and shall set the internal ref count to "1".]*/ + /*Tests_SRS_MESSAGE_02_019: [Message_Create shall clone the sourceProperties to a readonly CONSTMAP.] */ + /*Tests_SRS_MESSAGE_17_003: [Message_Create shall copy the source to a readonly CONSTBUFFER.]*/ + TEST_FUNCTION(Message_Create_happy_path) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake}; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Create(&fake, 1)); /*this is copying the buffer*/ + + STRICT_EXPECTED_CALL(mocks, ConstMap_Create((MAP_HANDLE)&fake)); /*this is copying the properties*/ + + ///act + auto r = Message_Create(&c); + + ///assert + ASSERT_IS_NOT_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(r); + } + + /*Tests_SRS_MESSAGE_02_004: [Mesages shall be allowed to be created from zero-size content.]*/ + TEST_FUNCTION(Message_Create_happy_path_zero_size_1) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + MESSAGE_CONFIG c = { 0, &fake, (MAP_HANDLE)&fake }; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Create(&fake, 0)); /* this is copying the (empty buffer)*/ + + STRICT_EXPECTED_CALL(mocks, ConstMap_Create((MAP_HANDLE)&fake)); /*this is copying the properties*/ + + ///act + auto r = Message_Create(&c); + + ///assert + ASSERT_IS_NOT_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(r); + } + + /*Tests_SRS_MESSAGE_02_004: [Mesages shall be allowed to be created from zero-size content.]*/ + TEST_FUNCTION(Message_Create_happy_path_zero_size_2) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + MESSAGE_CONFIG c = { 0, NULL, (MAP_HANDLE)&fake }; /*<---- this is NULL , in the testbefore it was non-NULL*/ + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Create(NULL, 0)); /* this is copying the (empty buffer)*/ + + STRICT_EXPECTED_CALL(mocks, ConstMap_Create((MAP_HANDLE)&fake)); /*this is copying the properties*/ + + ///act + auto r = Message_Create(&c); + + ///assert + ASSERT_IS_NOT_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(r); + } + + /*Tests_SRS_MESSAGE_02_005: [If Message_Create encounters an error while building the internal structures of the message, then it shall return NULL.]*/ + TEST_FUNCTION(Message_Create_zero_size_fails_when_Map_Clone_fails) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + MESSAGE_CONFIG c = { 0, NULL, (MAP_HANDLE)&fake }; /*<---- this is NULL , in the testbefore it was non-NULL*/ + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Create(NULL, 0)); /* this is copying the (empty buffer)*/ + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Destroy(IGNORED_PTR_ARG)) /* this is copying the (empty buffer)*/ + .IgnoreArgument(1); + + whenShallConstMap_Create_fail = 1; + STRICT_EXPECTED_CALL(mocks, ConstMap_Create((MAP_HANDLE)&fake)) /*this is copying the properties*/ + .IgnoreArgument(1); + + ///act + auto r = Message_Create(&c); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_02_005: [If Message_Create encounters an error while building the internal structures of the message, then it shall return NULL.]*/ + TEST_FUNCTION(Message_Create_zero_size_fails_when_malloc_fails) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + MESSAGE_CONFIG c = { 0, NULL, (MAP_HANDLE)&fake }; /*<---- this is NULL , in the testbefore it was non-NULL*/ + + whenShallmalloc_fail = 1; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + + ///act + auto r = Message_Create(&c); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_02_005: [If Message_Create encounters an error while building the internal structures of the message, then it shall return NULL.]*/ + TEST_FUNCTION(Message_Create_nonzero_size_fails_when_Map_Clone_fails) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake }; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Create(&fake, 1)); /* this is copying the buffer*/ + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + whenShallConstMap_Create_fail = 1; + STRICT_EXPECTED_CALL(mocks, ConstMap_Create((MAP_HANDLE)&fake)); /*this is copying the properties*/ + + ///act + auto r = Message_Create(&c); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_02_005: [If Message_Create encounters an error while building the internal structures of the message, then it shall return NULL.]*/ + TEST_FUNCTION(Message_Create_nonzero_size_fails_when_CONSTBUFFER_fails_1) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake }; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + whenShallCONSTBUFFER_Create_fail = 1; + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Create(&fake, 1)); /* this is copying the buffer*/ + + + ///act + auto r = Message_Create(&c); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_02_005: [If Message_Create encounters an error while building the internal structures of the message, then it shall return NULL.]*/ + TEST_FUNCTION(Message_Create_nonzero_size_fails_when_malloc_fails_2) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake }; + + whenShallmalloc_fail = 1; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + + ///act + auto r = Message_Create(&c); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /* Tests_SRS_MESSAGE_17_008: [ If cfg is NULL then Message_CreateFromBuffer shall return NULL.]*/ + TEST_FUNCTION(Message_CreateFromBuffer_with_NULL_parameter_fails) + { + ///arrange + CMessageMocks mocks; + + ///act + auto r = Message_CreateFromBuffer(NULL); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_17_009: [If field sourceContent of cfg is NULL, then Message_CreateFromBuffer shall fail and return NULL.]*/ + TEST_FUNCTION(Message_CreateFromBuffer_with_NULL_content_fails) + { + ///arrange + CMessageMocks mocks; + MESSAGE_BUFFER_CONFIG cfg = + { + NULL, + NULL + }; + /* typedef struct MESSAGE_BUFFER_CONFIG_TAG + { + CONSTBUFFER_HANDLE sourceContent; + MAP_HANDLE sourceProperties; + }MESSAGE_BUFFER_CONFIG;*/ + + ///act + auto r = Message_CreateFromBuffer(&cfg); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_17_010: [If field sourceProperties of cfg is NULL, then Message_CreateFromBuffer shall fail and return NULL.]*/ + TEST_FUNCTION(Message_CreateFromBuffer_with_NULL_properties_fails) + { + ///arrange + CMessageMocks mocks; + CONSTBUFFER_HANDLE buffer = CONSTBUFFER_Create(NULL, 0); + + mocks.ResetAllCalls(); + + MESSAGE_BUFFER_CONFIG cfg = + { + buffer, + NULL + }; + + ///act + auto r = Message_CreateFromBuffer(&cfg); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + CONSTBUFFER_Destroy(buffer); + + } + + /*Tests_SRS_MESSAGE_17_014: [On success, Message_CreateFromBuffer shall return a non-NULL handle and set the internal ref count to "1".]*/ + /*Tests_SRS_MESSAGE_17_012: [Message_CreateFromBuffer shall copy the sourceProperties to a readonly CONSTMAP.]*/ + /*Tests_SRS_MESSAGE_17_013: [Message_CreateFromBuffer shall clone the CONSTBUFFER sourceBuffer.]*/ + TEST_FUNCTION(Message_CreateFromBuffer_Success) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + CONSTBUFFER_HANDLE buffer = CONSTBUFFER_Create(&fake, 1); + MESSAGE_BUFFER_CONFIG cfg = + { + buffer, + (MAP_HANDLE)&fake + }; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Clone(buffer)); /*this is copying the buffer*/ + + STRICT_EXPECTED_CALL(mocks, ConstMap_Create((MAP_HANDLE)&fake)); /*this is copying the properties*/ + + + ///act + auto r = Message_CreateFromBuffer(&cfg); + + ///assert + ASSERT_IS_NOT_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(r); + CONSTBUFFER_Destroy(buffer); + + } + + /*Tests_SRS_MESSAGE_17_011: [If Message_CreateFromBuffer encounters an error while building the internal structures of the message, then it shall return NULL.]*/ + TEST_FUNCTION(Message_CreateFromBuffer_Allocation_Failed) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + CONSTBUFFER_HANDLE buffer = CONSTBUFFER_Create(&fake, 1); + MESSAGE_BUFFER_CONFIG cfg = + { + buffer, + (MAP_HANDLE)&fake + }; + + whenShallmalloc_fail = 1; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + + ///act + auto r = Message_CreateFromBuffer(&cfg); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + CONSTBUFFER_Destroy(buffer); + + } + + /*Tests_SRS_MESSAGE_17_011: [If Message_CreateFromBuffer encounters an error while building the internal structures of the message, then it shall return NULL.]*/ + TEST_FUNCTION(Message_CreateFromBuffer_Buffer_Clone_Failed) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + CONSTBUFFER_HANDLE buffer = CONSTBUFFER_Create(&fake, 1); + MESSAGE_BUFFER_CONFIG cfg = + { + buffer, + (MAP_HANDLE)&fake + }; + + whenShallCONSTBUFFER_Clone_fail = 1; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Clone(buffer)); /*this is copying the buffer*/ + + ///act + auto r = Message_CreateFromBuffer(&cfg); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + //cleanup + CONSTBUFFER_Destroy(buffer); + } + + /*Tests_SRS_MESSAGE_17_011: [If Message_CreateFromBuffer encounters an error while building the internal structures of the message, then it shall return NULL.]*/ + TEST_FUNCTION(Message_CreateFromBuffer_Properties_Copy_Failed) + { + ///arrange + CMessageMocks mocks; + unsigned char fake; + CONSTBUFFER_HANDLE buffer = CONSTBUFFER_Create(&fake, 1); + MESSAGE_BUFFER_CONFIG cfg = + { + buffer, + (MAP_HANDLE)&fake + }; + + whenShallConstMap_Create_fail = 1; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Clone(buffer)); /*this is copying the buffer*/ + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Destroy(buffer)); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Create((MAP_HANDLE)&fake)); /*this is copying the properties*/ + + + ///act + auto r = Message_CreateFromBuffer(&cfg); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + CONSTBUFFER_Destroy(buffer); + } + + /*Tests_SRS_MESSAGE_02_007: [If messageHandle is NULL then Message_Clone shall return NULL.] */ + TEST_FUNCTION(Message_Clone_with_NULL_argument_returns_NULL) + { + ///arrange + CMessageMocks mocks; + + ///act + auto r = Message_Clone(NULL); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_02_010: [Message_Clone shall return messageHandle.]*/ + /*Tests_SRS_MESSAGE_17_001: [Message_Clone shall clone the CONSTMAP handle.]*/ + /*Tests_SRS_MESSAGE_17_004: [Message_Clone shall clone the CONSTBUFFER handle]*/ + TEST_FUNCTION(Message_Clone_increments_ref_count_1) + { + ///arrange + CMessageMocks mocks; + MESSAGE_CONFIG c = {0, NULL, (MAP_HANDLE)&mocks}; + auto aMessage = Message_Create(&c); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto r = Message_Clone(aMessage); + + ///assert + ASSERT_ARE_EQUAL(void_ptr, aMessage, r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(r); + Message_Destroy(aMessage); + } + + /*Tests_SRS_MESSAGE_02_008: [Otherwise, Message_Clone shall increment the internal ref count.] */ + TEST_FUNCTION(Message_Clone_increments_ref_count_2) + { + ///arrange + CMessageMocks mocks; + MESSAGE_CONFIG c = { 0, NULL, (MAP_HANDLE)&mocks }; + auto aMessage = Message_Create(&c); + auto r = Message_Clone(aMessage); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + Message_Destroy(r); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(aMessage); + } + + /*Tests_SRS_MESSAGE_02_008: [Otherwise, Message_Clone shall increment the internal ref count.] */ + TEST_FUNCTION(Message_Clone_increments_ref_count_3) + { + ///arrange + CMessageMocks mocks; + MESSAGE_CONFIG c = { 0, NULL, (MAP_HANDLE)&mocks }; + auto aMessage = Message_Create(&c); + auto r = Message_Clone(aMessage); + Message_Destroy(r); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) /*only 1 because the message is 0 size*/ + .IgnoreArgument(1); + + ///act + Message_Destroy(aMessage); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_02_011: [If message is NULL then Message_GetProperties shall return NULL.] */ + TEST_FUNCTION(Message_GetProperties_with_NULL_messageHandle_returns_NULL) + { + ///arrange + CMessageMocks mocks; + mocks.ResetAllCalls(); + + ///act + auto r = Message_GetProperties(NULL); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_02_012: [Otherwise, Message_GetProperties shall shall clone and return the CONSTMAP handle representing the properties of the message.]*/ + TEST_FUNCTION(Message_GetProperties_happy_path) + { + ///arrange + CMessageMocks mocks; + MESSAGE_CONFIG c = { 0, NULL, (MAP_HANDLE)&mocks }; + auto aMessage = Message_Create(&c); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Clone(IGNORED_PTR_ARG)).IgnoreArgument(1); + + ///act + auto theProperties = Message_GetProperties(aMessage); + + ///assert + ASSERT_IS_NOT_NULL(theProperties); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(aMessage); + ConstMap_Destroy(theProperties); + } + + /*Tests_SRS_MESSAGE_02_013: [If message is NULL then Message_GetContent shall return NULL.] */ + TEST_FUNCTION(Message_GetContent_with_NULL_message_returns_NULL) + { + ///arrange + CMessageMocks mocks; + + ///act + auto content = Message_GetContent(NULL); + + ///assert + ASSERT_IS_NULL(content); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_02_014: [Otherwise, Message_GetContent shall return a non-NULL const pointer to a structure of type CONSTBUFFER.]*/ + /*Tests_SRS_MESSAGE_02_015: [The MESSAGE_CONTENT's field size shall have the same value as the cfg's field size.]*/ + /*Tests_SRS_MESSAGE_02_016: [The MESSAGE_CONTENT's field data shall compare equal byte-by-byte to the cfg's field source.]*/ + TEST_FUNCTION(Message_GetContent_with_non_NULL_message_zero_size_succeeds) + { + ///arrange + CMessageMocks mocks; + MESSAGE_CONFIG c = { 0, NULL, (MAP_HANDLE)&mocks }; + auto msg = Message_Create(&c); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_GetContent(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto content = Message_GetContent(msg); + + ///assert + ASSERT_IS_NOT_NULL(content); + ASSERT_ARE_EQUAL(size_t, 0, content->size); + ASSERT_IS_NULL(content->buffer); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(msg); + } + + /*Tests_SRS_MESSAGE_02_014: [Otherwise, Message_GetContent shall return a non-NULL const pointer to a structure of type CONSTBUFFER.]*/ + /*Tests_SRS_MESSAGE_02_015: [The MESSAGE_CONTENT's field size shall have the same value as the cfg's field size.]*/ + /*Tests_SRS_MESSAGE_02_016: [The MESSAGE_CONTENT's field data shall compare equal byte-by-byte to the cfg's field source.]*/ + TEST_FUNCTION(Message_GetContent_with_non_NULL_message_nonzero_size_succeeds) + { + ///arrange + CMessageMocks mocks; + char t ='3'; + MESSAGE_CONFIG c = { sizeof(t), (unsigned char*)&t, (MAP_HANDLE)&mocks }; + auto msg = Message_Create(&c); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_GetContent(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto content = Message_GetContent(msg); + + ///assert + ASSERT_IS_NOT_NULL(content); + ASSERT_ARE_EQUAL(size_t, 1, content->size); + ASSERT_ARE_EQUAL(int, 0, memcmp(content->buffer, &t, 1)); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(msg); + } + + /*Tests_SRS_MESSAGE_17_006: [If message is NULL then Message_GetContentHandle shall return NULL.]*/ + TEST_FUNCTION(Message_GetContentHandle_with_NULL_message_returns_NULL) + { + ///arrange + CMessageMocks mocks; + + ///act + auto content = Message_GetContentHandle(NULL); + + ///assert + ASSERT_IS_NULL(content); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_17_007: [Otherwise, Message_GetContentHandle shall shall clone and return the CONSTBUFFER_HANDLE representing the message content.]*/ + TEST_FUNCTION(Message_GetContentHandle_with_non_NULL_message_zero_size_succeeds) + { + ///arrange + CMessageMocks mocks; + MESSAGE_CONFIG c = { 0, NULL, (MAP_HANDLE)&mocks }; + auto msg = Message_Create(&c); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto content = Message_GetContentHandle(msg); + + ///assert + ASSERT_IS_NOT_NULL(content); + mocks.AssertActualAndExpectedCalls(); + + const CONSTBUFFER * contentBuffer = CONSTBUFFER_GetContent(content); + ASSERT_ARE_EQUAL(size_t, 0, contentBuffer->size); + ASSERT_IS_NULL(contentBuffer->buffer); + + ///cleanup + Message_Destroy(msg); + CONSTBUFFER_Destroy(content); + + } + + /*Tests_SRS_MESSAGE_17_007: [Otherwise, Message_GetContentHandle shall shall clone and return the CONSTBUFFER_HANDLE representing the message content.]*/ + TEST_FUNCTION(Message_GetContentHandle_with_non_NULL_message_nonzero_size_succeeds) + { + ///arrange + CMessageMocks mocks; + char t = '3'; + MESSAGE_CONFIG c = { sizeof(t), (unsigned char*)&t, (MAP_HANDLE)&mocks }; + auto msg = Message_Create(&c); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto content = Message_GetContentHandle(msg); + + ///assert + ASSERT_IS_NOT_NULL(content); + mocks.AssertActualAndExpectedCalls(); + + const CONSTBUFFER * contentBuffer = CONSTBUFFER_GetContent(content); + ASSERT_ARE_EQUAL(size_t, 1, contentBuffer->size); + ASSERT_ARE_EQUAL(int, 0, memcmp(contentBuffer->buffer, &t, 1)); + + ///cleanup + Message_Destroy(msg); + CONSTBUFFER_Destroy(content); + } + + /*Tests_SRS_MESSAGE_02_017: [If message is NULL then Message_Destroy shall do nothing.] */ + TEST_FUNCTION(Message_Destroy_with_NULL_argument_does_nothing) + { + ///arrange + CMessageMocks mocks; + + ///act + Message_Destroy(NULL); + + ///assert + + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MESSAGE_02_020: [Otherwise, Message_Destroy shall decrement the internal ref count of the message.] + /*Tests_SRS_MESSAGE_02_021: [If the ref count is zero then the allocated resources are freed.]*/ + /*Tests_SRS_MESSAGE_17_002: [Message_Destroy shall destroy the CONSTMAP properties.]*/ + /*Tests_SRS_MESSAGE_17_005: [Message_Destroy shall destroy the CONSTBUFFER.]*/ + TEST_FUNCTION(Message_Destroy_happy_path) + { + ///arrange + CMessageMocks mocks; + char t = '3'; + MESSAGE_CONFIG c = { sizeof(t), (unsigned char*)&t, (MAP_HANDLE)&mocks }; + auto msg = Message_Create(&c); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) /*this is the map*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Destroy(IGNORED_PTR_ARG)) /*this is the map*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + + ///act + Message_Destroy(msg); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } +END_TEST_SUITE(gwmessage_unittests) diff --git a/core/tests/gwmessage_unittests/main.c b/core/tests/gwmessage_unittests/main.c new file mode 100644 index 00000000..ece1450b --- /dev/null +++ b/core/tests/gwmessage_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(gwmessage_unittests, failedTestCount); + return failedTestCount; +} diff --git a/core/tests/message_bus_unittests/CMakeLists.txt b/core/tests/message_bus_unittests/CMakeLists.txt new file mode 100644 index 00000000..40be6ab6 --- /dev/null +++ b/core/tests/message_bus_unittests/CMakeLists.txt @@ -0,0 +1,22 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for message_bus_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName message_bus_unittests) +set(${theseTestsName}_cpp_files +${theseTestsName}.cpp +) + +set(${theseTestsName}_c_files + ../../src/message_bus.c +) + +set(${theseTestsName}_h_files +) + +include_directories(${GW_INC}) + +build_test_artifacts(${theseTestsName} ON) diff --git a/core/tests/message_bus_unittests/main.c b/core/tests/message_bus_unittests/main.c new file mode 100644 index 00000000..137fb9fa --- /dev/null +++ b/core/tests/message_bus_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(message_bus_unittests, failedTestCount); + return failedTestCount; +} diff --git a/core/tests/message_bus_unittests/message_bus_unittests.cpp b/core/tests/message_bus_unittests/message_bus_unittests.cpp new file mode 100644 index 00000000..29d0236a --- /dev/null +++ b/core/tests/message_bus_unittests/message_bus_unittests.cpp @@ -0,0 +1,2133 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include +#include + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" +#include "azure_c_shared_utility/condition.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/list.h" +#include "message.h" +#include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/refcount.h" + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit +#include "vector.c" +}; + +#include "message_bus.h" +#include "azure_c_shared_utility/lock.h" + +DEFINE_MICROMOCK_ENUM_TO_STRING(MESSAGE_BUS_RESULT, MESSAGE_BUS_RESULT_VALUES); + +static size_t currentmalloc_call; +static size_t whenShallmalloc_fail; + +static size_t currentVECTOR_create_call; +static size_t whenShallVECTOR_create_fail; + +static size_t currentVECTOR_push_back_call; +static size_t whenShallVECTOR_push_back_fail; + +static size_t currentVECTOR_find_if_call; +static size_t whenShallVECTOR_find_if_fail; + +static size_t currentlist_find_call; +static size_t whenShalllist_find_fail; + +static size_t currentlist_create_call; +static size_t whenShalllist_create_fail; + +static size_t currentlist_add_call; +static size_t whenShalllist_add_fail; + + +static size_t currentLock_Init_call; +static size_t whenShallLock_Init_fail; + +static size_t currentLock_call; +static size_t whenShallLock_fail; + +static size_t currentUnlock_call; + +static size_t currentCond_Init_call; +static size_t whenShallCond_Init_fail; + +static size_t currentCond_Post_call; +static size_t whenShallCond_Post_fail; + +static size_t currentThreadAPI_Create_call; +static size_t whenShallThreadAPI_Create_fail; + +typedef struct LIST_ITEM_INSTANCE_TAG +{ + const void* item; + void* next; +} LIST_ITEM_INSTANCE; + +typedef struct LIST_INSTANCE_TAG +{ + LIST_ITEM_INSTANCE* head; +} LIST_INSTANCE; + +static size_t current_list_index; +static const void *fake_list[10]; + +static bool shouldThreadAPI_Create_invoke_callback; +static THREAD_START_FUNC thread_func_to_call; +static void* thread_func_args; + +struct FakeModule_Receive_Call_Status +{ + MODULE_HANDLE module; + MESSAGE_HANDLE messageHandle; + bool was_called; +}; +static FakeModule_Receive_Call_Status call_status_for_FakeModule_Receive; + +// intercept variables for Condition_Wait mock +typedef COND_RESULT(*PFN_CONDITION_WAIT_INTERCEPT)(void); +static bool shouldIntercept_Condition_Wait; +static void* interceptArgs_for_Condition_Wait; +static PFN_CONDITION_WAIT_INTERCEPT intercept_for_Condition_Wait; + +static MODULE_HANDLE fake_module = (MODULE_HANDLE)0x42; +static MODULE_HANDLE FakeModule_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + return (MODULE_HANDLE)malloc(1); +} +static void FakeModule_Destroy(MODULE_HANDLE module) +{ + free(module); +} + +static void FakeModule_Receive(MODULE_HANDLE module, MESSAGE_HANDLE messageHandle) +{ + call_status_for_FakeModule_Receive.was_called = true; + ASSERT_ARE_EQUAL(void_ptr, module, call_status_for_FakeModule_Receive.module); + ASSERT_ARE_EQUAL(void_ptr, messageHandle, call_status_for_FakeModule_Receive.messageHandle); +} + +static MODULE_APIS fake_module_apis = +{ + FakeModule_Create, + FakeModule_Destroy, + FakeModule_Receive +}; + + +class RefCountObject +{ +private: + size_t ref_count; + +public: + RefCountObject() : ref_count(1) + { + } + + size_t inc_ref() + { + return ++ref_count; + } + + void dec_ref() + { + if (--ref_count == 0) + { + delete this; + } + } +}; + +TYPED_MOCK_CLASS(CMessageBusMocks, CGlobalMock) +{ +public: + + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2; + currentmalloc_call++; + if (currentmalloc_call == whenShallmalloc_fail) + { + result2 = NULL; + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + } + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_0(, LOCK_HANDLE, Lock_Init) + LOCK_HANDLE result2; + ++currentLock_Init_call; + if ((whenShallLock_Init_fail > 0) && + (currentLock_Init_call == whenShallLock_Init_fail)) + { + result2 = NULL; + } + else + { + result2 = (LOCK_HANDLE)malloc(1); + } + MOCK_METHOD_END(LOCK_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, LOCK_RESULT, Lock, LOCK_HANDLE, lock) + LOCK_RESULT result2; + ++currentLock_call; + if ((whenShallLock_fail > 0) && + (currentLock_call == whenShallLock_fail)) + { + result2 = LOCK_ERROR; + } + else + { + result2 = LOCK_OK; + } + MOCK_METHOD_END(LOCK_RESULT, result2) + + MOCK_STATIC_METHOD_1(, LOCK_RESULT, Unlock, LOCK_HANDLE, lock) + ASSERT_IS_TRUE((currentLock_call - currentUnlock_call) > 0); + ++currentUnlock_call; + auto result2 = LOCK_OK; + MOCK_METHOD_END(LOCK_RESULT, result2) + + MOCK_STATIC_METHOD_1(, LOCK_RESULT, Lock_Deinit, LOCK_HANDLE, lock) + free(lock); + auto result2 = LOCK_OK; + MOCK_METHOD_END(LOCK_RESULT, result2) + + MOCK_STATIC_METHOD_0(, COND_HANDLE, Condition_Init) + COND_HANDLE result2; + ++currentCond_Init_call; + if ((whenShallCond_Init_fail > 0) && + (currentCond_Init_call == whenShallCond_Init_fail)) + { + result2 = NULL; + } + else + { + result2 = (COND_HANDLE)malloc(2); + } + MOCK_METHOD_END(COND_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, COND_RESULT, Condition_Post, COND_HANDLE, handle) + COND_RESULT result2; + ++currentCond_Post_call; + if ((whenShallCond_Post_fail > 0) && + (currentCond_Post_call == whenShallCond_Post_fail)) + { + result2 = COND_ERROR; + } + else + { + result2 = COND_OK; + } + MOCK_METHOD_END(COND_RESULT, result2) + + MOCK_STATIC_METHOD_3(, COND_RESULT, Condition_Wait, COND_HANDLE, handle, LOCK_HANDLE, lock, int, timeout_milliseconds) + auto result2 = COND_OK; + if (shouldIntercept_Condition_Wait == true) + { + result2 = intercept_for_Condition_Wait(); + } + MOCK_METHOD_END(COND_RESULT, result2) + + MOCK_STATIC_METHOD_1(, void, Condition_Deinit, COND_HANDLE, handle) + free(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, VECTOR_HANDLE, VECTOR_create, size_t, elementSize) + VECTOR_HANDLE result2; + ++currentVECTOR_create_call; + if ((whenShallVECTOR_create_fail > 0) && + (currentVECTOR_create_call == whenShallVECTOR_create_fail)) + { + result2 = NULL; + } + else + { + result2 = BASEIMPLEMENTATION::VECTOR_create(elementSize); + } + MOCK_METHOD_END(VECTOR_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, VECTOR_destroy, VECTOR_HANDLE, vector) + BASEIMPLEMENTATION::VECTOR_destroy(vector); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, int, VECTOR_push_back, VECTOR_HANDLE, vector, const void*, elements, size_t, numElements) + int result2; + ++currentVECTOR_push_back_call; + if ((whenShallVECTOR_push_back_fail > 0) && + (currentVECTOR_push_back_call == whenShallVECTOR_push_back_fail)) + { + result2 = __LINE__; + } + else + { + result2 = BASEIMPLEMENTATION::VECTOR_push_back(vector, elements, numElements); + } + MOCK_METHOD_END(int, result2) + + MOCK_STATIC_METHOD_3(, void, VECTOR_erase, VECTOR_HANDLE, vector, void*, elements, size_t, numElements) + BASEIMPLEMENTATION::VECTOR_erase(vector, elements, numElements); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, void*, VECTOR_element, VECTOR_HANDLE, vector, size_t, index) + void* result2 = BASEIMPLEMENTATION::VECTOR_element(vector, index); + MOCK_METHOD_END(void*, result2) + + MOCK_STATIC_METHOD_1(, void*, VECTOR_front, VECTOR_HANDLE, vector) + void* result2 = BASEIMPLEMENTATION::VECTOR_front(vector); + MOCK_METHOD_END(void*, result2) + + MOCK_STATIC_METHOD_1(, void*, VECTOR_back, const VECTOR_HANDLE, vector) + void* result2 = BASEIMPLEMENTATION::VECTOR_back(vector); + MOCK_METHOD_END(void*, result2) + + MOCK_STATIC_METHOD_3(, void*, VECTOR_find_if, VECTOR_HANDLE, vector, PREDICATE_FUNCTION, pred, const void*, value) + void* result2; + ++currentVECTOR_find_if_call; + if ((whenShallVECTOR_find_if_fail > 0) && + (currentVECTOR_find_if_call == whenShallVECTOR_find_if_fail)) + { + result2 = NULL; + } + else + { + result2 = BASEIMPLEMENTATION::VECTOR_find_if(vector, pred, value); + } + MOCK_METHOD_END(void*, result2) + + MOCK_STATIC_METHOD_1(, size_t, VECTOR_size, VECTOR_HANDLE, vector) + size_t result2 = BASEIMPLEMENTATION::VECTOR_size(vector); + MOCK_METHOD_END(size_t, result2) + + MOCK_STATIC_METHOD_3(, THREADAPI_RESULT, ThreadAPI_Create, THREAD_HANDLE*, threadHandle, THREAD_START_FUNC, func, void*, arg) + THREADAPI_RESULT result2; + ++currentThreadAPI_Create_call; + if ((whenShallThreadAPI_Create_fail > 0) && + (currentThreadAPI_Create_call == whenShallThreadAPI_Create_fail)) + { + result2 = THREADAPI_ERROR; + } + else + { + *threadHandle = (THREAD_HANDLE*)malloc(3); + thread_func_to_call = func; + thread_func_args = arg; + + result2 = THREADAPI_OK; + if (shouldThreadAPI_Create_invoke_callback == true) + { + func(arg); + } + + + } + MOCK_METHOD_END(THREADAPI_RESULT, result2) + + MOCK_STATIC_METHOD_2(, THREADAPI_RESULT, ThreadAPI_Join, THREAD_HANDLE, threadHandle, int*, res) + free(threadHandle); + auto result2 = THREADAPI_OK; + MOCK_METHOD_END(THREADAPI_RESULT, result2) + + MOCK_STATIC_METHOD_1(, MESSAGE_HANDLE, Message_Create, const MESSAGE_CONFIG*, cfg) + MESSAGE_HANDLE result2 = (MESSAGE_HANDLE)(new RefCountObject()); + MOCK_METHOD_END(MESSAGE_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, MESSAGE_HANDLE, Message_Clone, MESSAGE_HANDLE, message) + ((RefCountObject*)message)->inc_ref(); + MOCK_METHOD_END(MESSAGE_HANDLE, message) + + MOCK_STATIC_METHOD_1(, void, Message_Destroy, MESSAGE_HANDLE, message) + ((RefCountObject*)message)->dec_ref(); + MOCK_VOID_METHOD_END() + + // list.h + + MOCK_STATIC_METHOD_0(, LIST_HANDLE, list_create) + ++currentlist_create_call; + LIST_HANDLE result1; + if (currentlist_create_call == whenShalllist_create_fail) + { + result1 = NULL; + } + else + { + result1 = (LIST_HANDLE)(new RefCountObject()); + } + MOCK_METHOD_END(LIST_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, void, list_destroy, LIST_HANDLE, list) + ((RefCountObject*)list)->dec_ref(); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, LIST_ITEM_HANDLE, list_add, LIST_HANDLE, list, const void*, item) + LIST_ITEM_HANDLE result1; + ++currentlist_add_call; + if (currentlist_add_call == whenShalllist_add_fail) + { + result1 = NULL; + } + else + { + if ((list == NULL) || + (item == NULL)) + { + result1 = NULL; + } + else + { + fake_list[current_list_index++] = item; + result1 = (LIST_ITEM_HANDLE)item; + } + } + MOCK_METHOD_END(LIST_ITEM_HANDLE, result1) + + MOCK_STATIC_METHOD_2(, int, list_remove, LIST_HANDLE, list, LIST_ITEM_HANDLE, item) + int result2; + if ((list == NULL) || + (item == NULL)) + { + result2 = __LINE__; + } + else + { + /* do I need anything more compicated here? */ + result2 = 0; + } + + MOCK_METHOD_END(int, result2) + + MOCK_STATIC_METHOD_1(, LIST_ITEM_HANDLE, list_get_head_item, LIST_HANDLE, list) + LIST_ITEM_HANDLE result1; + if (list == NULL) + { + result1 = NULL; + } + else + { + result1 = (LIST_ITEM_HANDLE)fake_list[0]; + } + MOCK_METHOD_END(LIST_ITEM_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, LIST_ITEM_HANDLE, list_get_next_item, LIST_ITEM_HANDLE, item_handle) + LIST_ITEM_HANDLE result1; + if (item_handle == NULL) + { + result1 = NULL; + } + else + { + result1 = (LIST_ITEM_HANDLE)(fake_list[current_list_index]); + } + MOCK_METHOD_END(LIST_ITEM_HANDLE, result1) + + MOCK_STATIC_METHOD_3(, LIST_ITEM_HANDLE, list_find, LIST_HANDLE, list, LIST_MATCH_FUNCTION, match_function, const void*, match_context) + LIST_ITEM_HANDLE result1; + currentlist_find_call++; + if (currentlist_find_call == whenShalllist_find_fail) + { + result1 = NULL; + } + else + { + if ((list == NULL) || + (match_function == NULL)) + { + result1 = NULL; + } + else if ((void*)fake_module == (void*)match_context) + { + result1 = (LIST_ITEM_HANDLE)fake_list[0]; + } + else + { + result1 = NULL; + for (size_t i = 0; i < current_list_index; i++) + { + if ((void*)fake_list[i] == (void*)match_context) + { + result1 = (LIST_ITEM_HANDLE)fake_list[i]; + break; + } + } + } + } + MOCK_METHOD_END(LIST_ITEM_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, const void*, list_item_get_value, LIST_ITEM_HANDLE, item_handle) + const void* result1; + if (item_handle == NULL) + { + result1 = NULL; + } + else + { + result1 = item_handle; + } + MOCK_METHOD_END(const void*, result1) +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , void, gballoc_free, void*, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_0(CMessageBusMocks, , LOCK_HANDLE, Lock_Init); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , LOCK_RESULT, Lock, LOCK_HANDLE, lock); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , LOCK_RESULT, Unlock, LOCK_HANDLE, lock); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , LOCK_RESULT, Lock_Deinit, LOCK_HANDLE, lock); + +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , VECTOR_HANDLE, VECTOR_create, size_t, elementSize); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , void, VECTOR_destroy, VECTOR_HANDLE, vector); +DECLARE_GLOBAL_MOCK_METHOD_3(CMessageBusMocks, , int, VECTOR_push_back, VECTOR_HANDLE, vector, const void*, elements, size_t, numElements); +DECLARE_GLOBAL_MOCK_METHOD_3(CMessageBusMocks, , void, VECTOR_erase, VECTOR_HANDLE, vector, void*, elements, size_t, numElements); +DECLARE_GLOBAL_MOCK_METHOD_2(CMessageBusMocks, , void*, VECTOR_element, VECTOR_HANDLE, vector, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , void*, VECTOR_front, VECTOR_HANDLE, vector); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , void*, VECTOR_back, VECTOR_HANDLE, vector); +DECLARE_GLOBAL_MOCK_METHOD_3(CMessageBusMocks, , void*, VECTOR_find_if, VECTOR_HANDLE, vector, PREDICATE_FUNCTION, pred, const void*, value); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , size_t, VECTOR_size, VECTOR_HANDLE, vector); + +DECLARE_GLOBAL_MOCK_METHOD_0(CMessageBusMocks, , COND_HANDLE, Condition_Init); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , COND_RESULT, Condition_Post, COND_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CMessageBusMocks, , COND_RESULT, Condition_Wait, COND_HANDLE, handle, LOCK_HANDLE, lock, int, timeout_milliseconds); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , void, Condition_Deinit, COND_HANDLE, handle); + +DECLARE_GLOBAL_MOCK_METHOD_3(CMessageBusMocks, , THREADAPI_RESULT, ThreadAPI_Create, THREAD_HANDLE*, threadHandle, THREAD_START_FUNC, func, void*, arg); +DECLARE_GLOBAL_MOCK_METHOD_2(CMessageBusMocks, , THREADAPI_RESULT, ThreadAPI_Join, THREAD_HANDLE, threadHandle, int*, res); + +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , MESSAGE_HANDLE, Message_Create, const MESSAGE_CONFIG*, cfg); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , MESSAGE_HANDLE, Message_Clone, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , void, Message_Destroy, MESSAGE_HANDLE, message); + +// list.h +DECLARE_GLOBAL_MOCK_METHOD_0(CMessageBusMocks, , LIST_HANDLE, list_create); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , void, list_destroy, LIST_HANDLE, list); +DECLARE_GLOBAL_MOCK_METHOD_2(CMessageBusMocks, , LIST_ITEM_HANDLE, list_add, LIST_HANDLE, list, const void*, item); +DECLARE_GLOBAL_MOCK_METHOD_2(CMessageBusMocks, , int, list_remove, LIST_HANDLE, list, LIST_ITEM_HANDLE, item_handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , LIST_ITEM_HANDLE, list_get_head_item, LIST_HANDLE, list); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , LIST_ITEM_HANDLE, list_get_next_item, LIST_ITEM_HANDLE, item_handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CMessageBusMocks, , LIST_ITEM_HANDLE, list_find, LIST_HANDLE, list, LIST_MATCH_FUNCTION, match_function, const void*, match_context); +DECLARE_GLOBAL_MOCK_METHOD_1(CMessageBusMocks, , const void*, list_item_get_value, LIST_ITEM_HANDLE, item_handle); + + +BEGIN_TEST_SUITE(message_bus_unittests) + +TEST_SUITE_INITIALIZE(TestClassInitialize) +{ + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); +} + +TEST_SUITE_CLEANUP(TestClassCleanup) +{ + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); +} + +TEST_FUNCTION_INITIALIZE(TestMethodInitialize) +{ + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + + currentmalloc_call = 0; + whenShallmalloc_fail = 0; + + currentVECTOR_create_call = 0; + whenShallVECTOR_create_fail = 0; + + currentVECTOR_push_back_call = 0; + whenShallVECTOR_push_back_fail = 0; + + currentVECTOR_find_if_call = 0; + whenShallVECTOR_find_if_fail = 0; + + currentLock_Init_call = 0; + whenShallLock_Init_fail = 0; + + currentlist_find_call = 0; + whenShalllist_find_fail = 0; + + currentlist_create_call = 0; + whenShalllist_create_fail = 0; + + currentlist_add_call = 0; + whenShalllist_add_fail = 0; + + currentLock_call = 0; + whenShallLock_fail = 0; + + currentUnlock_call = 0; + + currentCond_Init_call = 0; + whenShallCond_Init_fail = 0; + + currentCond_Post_call = 0; + whenShallCond_Post_fail = 0; + + currentThreadAPI_Create_call = 0; + whenShallThreadAPI_Create_fail = 0; + + current_list_index = 0; + for (int l = 0; l < 10; l++) + { + fake_list[l] = NULL; + } + + shouldThreadAPI_Create_invoke_callback = false; + thread_func_to_call = NULL; + thread_func_args = NULL; + + shouldIntercept_Condition_Wait = false; + interceptArgs_for_Condition_Wait = NULL; + intercept_for_Condition_Wait = NULL; + + call_status_for_FakeModule_Receive.messageHandle = NULL; + call_status_for_FakeModule_Receive.module = NULL; + call_status_for_FakeModule_Receive.was_called = false; +} + +TEST_FUNCTION_CLEANUP(TestMethodCleanup) +{ + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } +} + +//Tests_SRS_MESSAGE_BUS_13_001: [This API shall yield a MESSAGE_BUS_HANDLE representing the newly created message bus.This handle value shall not be equal to NULL when the API call is successful.] +//Tests_SRS_MESSAGE_BUS_13_007: [MessageBus_Create shall initialize MESSAGE_BUS_HANDLE_DATA::modules with a valid VECTOR_HANDLE.] +//Tests_SRS_MESSAGE_BUS_13_023: [MessageBus_Create shall initialize MESSAGE_BUS_HANDLE_DATA::modules_lock with a valid LOCK_HANDLE.] +TEST_FUNCTION(MessageBus_Create_succeeds) +{ + ///arrange + CMessageBusMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_create()); + STRICT_EXPECTED_CALL(mocks, Lock_Init()); + + ///act + auto r = MessageBus_Create(); + + ///assert + ASSERT_IS_NOT_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(r); +} + +//Tests_SRS_MESSAGE_BUS_13_003: [This function shall return NULL if an underlying API call to the platform causes an error.] +/*Tests_SRS_MESSAGE_BUS_13_067: [ MessageBus_Create shall malloc a new instance of MESSAGE_BUS_HANDLE_DATA and return NULL if it fails. ]*/ +TEST_FUNCTION(MessageBus_Create_fails_when_malloc_fails) +{ + ///arrange + + CMessageBusMocks mocks; + whenShallmalloc_fail = 1; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + + ///act + auto r = MessageBus_Create(); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_003: [This function shall return NULL if an underlying API call to the platform causes an error.] +TEST_FUNCTION(MessageBus_Create_fails_when_list_create_fails) +{ + ///arrange + + CMessageBusMocks mocks; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + whenShalllist_create_fail = 1; + STRICT_EXPECTED_CALL(mocks, list_create()); + + ///act + auto r = MessageBus_Create(); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +TEST_FUNCTION(MessageBus_Create_fails_when_Lock_Init_fails) +{ + ///arrange + + CMessageBusMocks mocks; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the structure*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_create()); + STRICT_EXPECTED_CALL(mocks, list_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + whenShallLock_Init_fail = 1; + STRICT_EXPECTED_CALL(mocks, Lock_Init()); + + ///act + auto r = MessageBus_Create(); + + ///assert + ASSERT_IS_NULL(r); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_038: [If bus or module or module_apis is NULL the function shall return MESSAGE_BUS_INVALIDARG.] +TEST_FUNCTION(MessageBus_AddModule_fails_with_null_bus) +{ + ///arrange + CMessageBusMocks mocks; + + ///act + auto result = MessageBus_AddModule(NULL, (MODULE_HANDLE)0x1, (const MODULE_APIS*)0x1); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_INVALIDARG); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_038: [If bus or module or module_apis is NULL the function shall return MESSAGE_BUS_INVALIDARG.] +TEST_FUNCTION(MessageBus_AddModule_fails_with_null_module) +{ + ///arrange + CMessageBusMocks mocks; + + ///act + auto result = MessageBus_AddModule((MESSAGE_BUS_HANDLE)0x1, NULL, (const MODULE_APIS*)0x1); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_INVALIDARG); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_038: [If bus or module or module_apis is NULL the function shall return MESSAGE_BUS_INVALIDARG.] +TEST_FUNCTION(MessageBus_AddModule_fails_with_null_module_apis) +{ + ///arrange + CMessageBusMocks mocks; + + ///act + auto result = MessageBus_AddModule((MESSAGE_BUS_HANDLE)0x1, (MODULE_HANDLE)0x1, NULL); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_INVALIDARG); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_047: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_AddModule_fails_when_alloc_module_info_fails) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + // this is for the MessageBus_AddModule call + whenShallmalloc_fail = currentmalloc_call + 1; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module_info*/ + .IgnoreArgument(1); + + ///act + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); +} + +TEST_FUNCTION(MessageBus_AddModule_fails_when_VECTOR_create_fails) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + // this is for the MessageBus_AddModule call + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module_info*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + whenShallVECTOR_create_fail = currentVECTOR_create_call + 1; + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + ///act + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_047: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_AddModule_fails_when_Lock_Init_fails) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + // this is for the MessageBus_AddModule call + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module_info*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + whenShallLock_Init_fail = currentLock_Init_call + 1; + STRICT_EXPECTED_CALL(mocks, Lock_Init()); + + ///act + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_047: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_AddModule_fails_when_Condition_Init_fails) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + // this is for the MessageBus_AddModule call + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module_info*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock_Init()); + STRICT_EXPECTED_CALL(mocks, Lock_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + whenShallCond_Init_fail = 1; + STRICT_EXPECTED_CALL(mocks, Condition_Init()); + + ///act + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_047: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_AddModule_fails_when_list_add_fails) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + // this is for the MessageBus_AddModule call + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module_info*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock_Init()); + STRICT_EXPECTED_CALL(mocks, Lock_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Init()); + STRICT_EXPECTED_CALL(mocks, Condition_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + whenShalllist_add_fail = 1; + STRICT_EXPECTED_CALL(mocks, list_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_047: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_AddModule_fails_when_ThreadAPI_Create_fails) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + // this is for the MessageBus_AddModule call + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module_info*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock_Init()); + STRICT_EXPECTED_CALL(mocks, Lock_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Init()); + STRICT_EXPECTED_CALL(mocks, Condition_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, list_remove(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + whenShallThreadAPI_Create_fail = 1; + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_047: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_AddModule_fails_when_lock_on_modules_lock_fails) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + // this is for the MessageBus_AddModule call + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module_info*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock_Init()); + whenShallLock_fail = 1; + STRICT_EXPECTED_CALL(mocks, Lock_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Init()); + STRICT_EXPECTED_CALL(mocks, Condition_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); +} + + + +//Tests_SRS_MESSAGE_BUS_13_097 : [The function shall assign module_apis to MESSAGE_BUS_MODULEINFO::module_apis.] +//Tests_SRS_MESSAGE_BUS_13_107 : [The function shall assign the module handle to MESSAGE_BUS_MODULEINFO::module.] +//Tests_SRS_MESSAGE_BUS_13_098 : [The function shall initialize MESSAGE_BUS_MODULEINFO::mq with a valid vector handle.] +//Tests_SRS_MESSAGE_BUS_13_099 : [The function shall initialize MESSAGE_BUS_MODULEINFO::mq_lock with a valid lock handle.] +//Tests_SRS_MESSAGE_BUS_13_100 : [The function shall initialize MESSAGE_BUS_MODULEINFO::mq_cond with a valid condition handle.] +//Tests_SRS_MESSAGE_BUS_13_101 : [ The function shall assign 0 to MESSAGE_BUS_MODULEINFO::quit_worker. ] +//Tests_SRS_MESSAGE_BUS_13_102 : [The function shall create a new thread for the module by calling ThreadAPI_Create using module_publish_worker as the thread callback and using the newly allocated MESSAGE_BUS_MODULEINFO object as the thread context.] +//Tests_SRS_MESSAGE_BUS_13_039 : [This function shall acquire the lock on MESSAGE_BUS_HANDLE_DATA::modules_lock.] +//Tests_SRS_MESSAGE_BUS_13_045 : [MessageBus_AddModule shall append the new instance of MESSAGE_BUS_MODULEINFO to MESSAGE_BUS_HANDLE_DATA::modules.] +//Tests_SRS_MESSAGE_BUS_13_046 : [This function shall release the lock on MESSAGE_BUS_HANDLE_DATA::modules_lock.] +//Tests_SRS_MESSAGE_BUS_13_047 : [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_AddModule_succeeds) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + // this is for the MessageBus_AddModule call + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module_info*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, Lock_Init()); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Init()); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_OK); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_RemoveModule(bus, fake_module); + MessageBus_Destroy(bus); +} + +struct Condition_Wait_Callback_Input +{ + MESSAGE_BUS_HANDLE bus; + MESSAGE_HANDLE message; +}; + +static COND_RESULT module_publish_worker_calls_module_receive_Condition_Wait2(void) +{ + Condition_Wait_Callback_Input* input = (Condition_Wait_Callback_Input*)interceptArgs_for_Condition_Wait; + + // cause the worker to quit during the next iteration; + // first we get the modules vector from the bus handle + + // We added one module for the test, that's our fake list... + unsigned char* module_info = (unsigned char*)fake_list[0]; + sig_atomic_t* quit_worker = (sig_atomic_t*)(module_info + BUS_offsetof_quit_worker); + *quit_worker = 1; + + return COND_OK; +} + +static COND_RESULT module_publish_worker_calls_module_receive_Condition_Wait(void) +{ + // publish a message on to the bus + Condition_Wait_Callback_Input* input = (Condition_Wait_Callback_Input*)interceptArgs_for_Condition_Wait; + auto result = MessageBus_Publish(input->bus, input->message); + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, MESSAGE_BUS_OK, result); + + // schedule module_publish_worker_calls_module_receive_Condition_Wait2 to be + // called during the next intercept of the Condition_Wait mock + intercept_for_Condition_Wait = module_publish_worker_calls_module_receive_Condition_Wait2; + + return COND_OK; +} + +// Tests_SRS_MESSAGE_BUS_13_089: [ This function shall acquire the lock on module_info->mq_lock. ] +// Tests_SRS_MESSAGE_BUS_13_068: [ This function shall run a loop that keeps running while module_info->quit_worker is equal to 0. ] +// Tests_SRS_MESSAGE_BUS_13_071: [ For every iteration of the loop the function will first wait on module_info->mq_cond using module_info->mq_lock as the corresponding mutex to be used by the condition variable. ] +// Tests_SRS_MESSAGE_BUS_13_090: [ When module_info->mq_cond has been signaled this function shall kick off another loop predicated on module_info->quit_worker being equal to 0 and module_info->mq not being empty. This thread has the lock on module_info->mq_lock at this point. ] +// Tests_SRS_MESSAGE_BUS_13_069: [ The function shall dequeue a message from the module's message queue. ] +// Tests_SRS_MESSAGE_BUS_13_091: [ The function shall unlock module_info->mq_lock. ] +// Tests_SRS_MESSAGE_BUS_13_092: [ The function shall deliver the message to the module's callback function via module_info->module_apis. ] +// Tests_SRS_MESSAGE_BUS_13_093: [ The function shall destroy the message that was dequeued by calling Message_Destroy. ] +// Tests_SRS_MESSAGE_BUS_13_094: [ The function shall re-acquire the lock on module_info->mq_lock. ] +// Tests_SRS_MESSAGE_BUS_13_095: [ When the function exits the outer loop predicated on module_info->quit_worker being 0 it shall unlock module_info->mq_lock before exiting from the function. ] +// Tests_SRS_MESSAGE_BUS_13_026: [ This function shall assign user_data to a local variable called module_info of type MESSAGE_BUS_MODULEINFO*. ] +// Tests_SRS_MESSAGE_BUS_04_001: [** This function shall immediately start processing messages when `module->mq` is not empty without waiting on `module->mq_cond`.] +TEST_FUNCTION(module_publish_worker_calls_module_receive) +{ + /** + * This test warrants some documentation as to how it works because + * we are going to great lengths here to test threaded code without + * using threads. Here's what happens: + * + * [1] The `ThreadAPI_Create` mock will directly call the callback + * function when `shouldThreadAPI_Create_invoke_callback` is `true`. + * We set this variable to `true` in this test. + * + * [2] We call `MessageBus_AddModule` in this test. That function will eventually + * call `ThreadAPI_Create`. `ThreadAPI_Create` will call the + * `module_publish_worker` function which, among other things, will + * call `Condition_Wait` which we have also mocked. + * + * [3] When we receive the call to the mocked version of `Condition_Wait` + * from `module_publish_worker` we have it invoke a function pointed at + * by the variable `intercept_for_Condition_Wait` when `shouldIntercept_Condition_Wait` + * is `true`. We initialize it to true and assign + * `module_publish_worker_calls_module_receive_Condition_Wait` to `intercept_for_Condition_Wait`. + * + * [4] Here's what the call stack looks like right now: + * + * MessageBus_AddModule -> ThreadAPI_Create -> module_publish_worker -> + * Condition_Wait -> module_publish_worker_calls_module_receive_Condition_Wait + * + * [5] `module_publish_worker_calls_module_receive_Condition_Wait` uses the data pointed at by + * the global variable `interceptArgs_for_Condition_Wait` to get a handle to the + * message bus and the message to be published on the message bus. We assign a pointer to a local + * variable of type `Condition_Wait_Callback_Input` in the test case + * (i.e. `module_publish_worker_calls_module_receive`) to 'interceptArgs_for_Condition_Wait'. + * + * [6] `module_publish_worker_calls_module_receive_Condition_Wait` publishes a message on to + * the message bus via `MessageBus_Publish` and replaces `intercept_for_Condition_Wait` so that it + * points to the function `module_publish_worker_calls_module_receive_Condition_Wait2`. + * + * [7] Control then goes back to `module_publish_worker` which proceeds to consume the newly + * enqueued message and delivers it to the fake module. `FakeModule_Receive` gets called + * where we verify that the module handle and the message handle have expected values. + * + * [8] `module_publish_worker` then loops around and calls `Condition_Wait` again. And this + * time the `module_publish_worker_calls_module_receive_Condition_Wait2` function ends up + * being called. In this function our goal is to assign `1` to the `quit_worker` + * variable in the module's `MESSAGE_BUS_MODULEINFO` struct. Since we don't have access to the + * structs backing the message bus we use the global variables `MESSAGE_BUS_offsetof_modules` and + * `MESSAGE_BUS_offsetof_quit_worker` provided by the message bus API to figure out the location where + * the `quit_worker` variable resides in memory and assign `1` to it. + * + * [9] Since `quit_worker` is now `1`, `module_publish_worker` will exit its processing loop + * and the entire stack will unwind and control comes back to the test case when + * `MessageBus_AddModule` returns. + */ + + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + + // make ThreadAPI_Create mock call the callback function + shouldThreadAPI_Create_invoke_callback = true; + + // we want to intercept Condition_Wait when it is called + shouldIntercept_Condition_Wait = true; + Condition_Wait_Callback_Input input{ bus, NULL }; + interceptArgs_for_Condition_Wait = (void*)&input; + intercept_for_Condition_Wait = module_publish_worker_calls_module_receive_Condition_Wait; + + // setup fake module's validation data + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake }; + auto message = Message_Create(&c); + input.message = message; + call_status_for_FakeModule_Receive.module = fake_module; + call_status_for_FakeModule_Receive.messageHandle = message; + + mocks.ResetAllCalls(); + + // this is for MessageBus_Publish + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_head_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_next_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_item_get_value(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, Message_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Post(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + // this is for the MessageBus_AddModule call + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module_info*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, Lock_Init()); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Init()); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + // this is for module_publish_worker + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Wait(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, Condition_Wait(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_front(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_erase(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + + ///act + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_OK); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(message); + MessageBus_RemoveModule(bus, fake_module); + MessageBus_Destroy(bus); +} + +/*Tests_SRS_MESSAGE_BUS_02_004: [ If acquiring the lock fails, then module_publish_worker shall return. ]*/ +TEST_FUNCTION(module_publish_worker_fails_when_first_lock_fails) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + + // make ThreadAPI_Create mock call the callback function + shouldThreadAPI_Create_invoke_callback = true; + + // we want to intercept Condition_Wait when it is called + shouldIntercept_Condition_Wait = true; + Condition_Wait_Callback_Input input{ bus, NULL }; + interceptArgs_for_Condition_Wait = (void*)&input; + intercept_for_Condition_Wait = module_publish_worker_calls_module_receive_Condition_Wait; + + mocks.ResetAllCalls(); + + // this is for the MessageBus_AddModule call + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module_info*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, Lock_Init()); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Init()); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + // this is for module_publish_worker + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn(LOCK_ERROR); + + ///act + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_OK); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_RemoveModule(bus, fake_module); + MessageBus_Destroy(bus); +} + + +// Tests_SRS_MESSAGE_BUS_04_001: [** This function shall immediately start processing messages when `module->mq` is not empty without waiting on `module->mq_cond`.] +TEST_FUNCTION(module_publish_worker_clean_message_queue_before_waiting_for_condition) +{ + //This test follow the same guideline of test: module_publish_worker_calls_module_receive, with the exception that + //module_publish_worker IS NOT called when you call ThreadAPI_Create. This is to simulate the scenario where I have a message published on the + //module before thread starts. + + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + + // we want to intercept Condition_Wait when it is called + shouldIntercept_Condition_Wait = true; + Condition_Wait_Callback_Input input{ bus, NULL }; + interceptArgs_for_Condition_Wait = (void*)&input; + intercept_for_Condition_Wait = module_publish_worker_calls_module_receive_Condition_Wait; + + // setup fake module's validation data + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake }; + auto message = Message_Create(&c); + + input.message = message; + call_status_for_FakeModule_Receive.module = fake_module; + call_status_for_FakeModule_Receive.messageHandle = message; + + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_OK); + + result = MessageBus_Publish(bus, message); + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, MESSAGE_BUS_OK, result); + + mocks.ResetAllCalls(); + + // this is for module_publish_worker + + //Calls for the first message before calling Condition_Wait + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_front(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_erase(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + + + //Calls for when Condition_Wait is Intercepted + // this is for MessageBus_Publish + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_head_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_next_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_item_get_value(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, Message_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Post(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + // this is for module_publish_worker + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Wait(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, Condition_Wait(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_front(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_erase(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + thread_func_to_call(thread_func_args); + + ///assert + + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(message); + MessageBus_RemoveModule(bus, fake_module); + MessageBus_Destroy(bus); +} + + +//Tests_SRS_MESSAGE_BUS_13_048: [If bus or module is NULL the function shall return MESSAGE_BUS_INVALIDARG.] +TEST_FUNCTION(MessageBus_RemoveModule_fails_with_null_bus) +{ + ///arrange + CMessageBusMocks mocks; + + ///act + auto r1 = MessageBus_RemoveModule(NULL, (MODULE_HANDLE)0x1); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, r1, MESSAGE_BUS_INVALIDARG); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_048: [If bus or module is NULL the function shall return MESSAGE_BUS_INVALIDARG.] +TEST_FUNCTION(MessageBus_RemoveModule_fails_with_null_module) +{ + ///arrange + CMessageBusMocks mocks; + + ///act + auto r1 = MessageBus_RemoveModule((MESSAGE_BUS_HANDLE)0x1, NULL); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, r1, MESSAGE_BUS_INVALIDARG); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_053: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_RemoveModule_fails_when_Lock_fails) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + mocks.ResetAllCalls(); + + // this is for the MessageBus_RemoveModule call + whenShallLock_fail = currentLock_call + 1; + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + result = MessageBus_RemoveModule(bus, fake_module); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_RemoveModule(bus, fake_module); + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_050: [MessageBus_RemoveModule shall unlock MESSAGE_BUS_HANDLE_DATA::modules_lock and return MESSAGE_BUS_MODULENOTFOUND if the module is not found in MESSAGE_BUS_HANDLE_DATA::modules.] +TEST_FUNCTION(MessageBus_RemoveModule_fails_when_list_find_fails) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + mocks.ResetAllCalls(); + + // this is for the MessageBus_RemoveModule call + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + whenShalllist_find_fail = 1; + STRICT_EXPECTED_CALL(mocks, list_find(IGNORED_PTR_ARG, IGNORED_PTR_ARG, fake_module)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + ///act + result = MessageBus_RemoveModule(bus, fake_module); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_RemoveModule(bus, fake_module); + MessageBus_Destroy(bus); +} + +/*Tests_SRS_MESSAGE_BUS_02_002: [ If locking fails, then terminating the thread shall not be attempted (signalling the condition and joining the thread). ]*/ +//Tests_SRS_MESSAGE_BUS_13_050: [MessageBus_RemoveModule shall unlock MESSAGE_BUS_HANDLE_DATA::modules_lock and return MESSAGE_BUS_MODULENOTFOUND if the module is not found in MESSAGE_BUS_HANDLE_DATA::modules.] +TEST_FUNCTION(MessageBus_RemoveModule_fails_when_lock_mq_lock_fails) +{ + { + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + mocks.ResetAllCalls(); + + // this is for the MessageBus_RemoveModule call + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, list_find(IGNORED_PTR_ARG, IGNORED_PTR_ARG, fake_module)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, list_item_get_value(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) /*this is the lock protecting mq_lock*/ + .IgnoreArgument(1) + .SetFailReturn(LOCK_ERROR); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Join(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_remove(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + + ///act + result = MessageBus_RemoveModule(bus, fake_module); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_OK); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); + } +} +//Tests_SRS_MESSAGE_BUS_13_088 : [This function shall acquire the lock on MESSAGE_BUS_HANDLE_DATA::modules_lock.] +//Tests_SRS_MESSAGE_BUS_13_049 : [MessageBus_RemoveModule shall perform a linear search for module in MESSAGE_BUS_HANDLE_DATA::modules.] +//Tests_SRS_MESSAGE_BUS_13_050 : [MessageBus_RemoveModule shall unlock MESSAGE_BUS_HANDLE_DATA::modules_lock and return MESSAGE_BUS_MODULENOTFOUND if the module is not found in MESSAGE_BUS_HANDLE_DATA::modules.] +//Tests_SRS_MESSAGE_BUS_13_052 : [The function shall remove the module from MESSAGE_BUS_HANDLE_DATA::modules.] +//Tests_SRS_MESSAGE_BUS_13_054 : [This function shall release the lock on MESSAGE_BUS_HANDLE_DATA::modules_lock.] +/*Tests_SRS_MESSAGE_BUS_02_001: [ MessageBus_RemoveModule shall lock `MESSAGE_BUS_MODULEINFO::mq_lock`. ]*/ +/*Tests_SRS_MESSAGE_BUS_02_003: [ After signaling the condition, MessageBus_RemoveModule shall unlock MESSAGE_BUS_MODULEINFO::mq_lock. ]*/ +//Tests_SRS_MESSAGE_BUS_13_103 : [The function shall assign 1 to MESSAGE_BUS_MODULEINFO::quit_worker.] +//Tests_SRS_MESSAGE_BUS_17_001: [The function shall signal MESSAGE_BUS_MODULEINFO::mq_cond to release module from waiting.] +//Tests_SRS_MESSAGE_BUS_13_104 : [The function shall wait for the module's thread to exit by joining MESSAGE_BUS_MODULEINFO::thread via ThreadAPI_Join. ] +//Tests_SRS_MESSAGE_BUS_13_057 : [The function shall free all members of the MESSAGE_BUS_MODULEINFO object.] +//Tests_SRS_MESSAGE_BUS_13_053 : [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_RemoveModule_succeeds) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + mocks.ResetAllCalls(); + + // this is for the MessageBus_RemoveModule call + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, list_find(IGNORED_PTR_ARG, IGNORED_PTR_ARG, fake_module)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, list_item_get_value(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) /*this is the lock protecting mq_lock*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Post(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Join(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_remove(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + + ///act + result = MessageBus_RemoveModule(bus, fake_module); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_OK); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); +} + +/*Tests_SRS_MESSAGE_BUS_13_056: [If MESSAGE_BUS_MODULEINFO::mq is not empty then this function shall call Message_Destroy on every message still left in the collection.]*/ +TEST_FUNCTION(MessageBus_RemoveModule_with_msg_succeeds) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake }; + auto message = Message_Create(&c); + auto result2 = MessageBus_Publish(bus, message); + Message_Destroy(message); + mocks.ResetAllCalls(); + + + // this is for the MessageBus_RemoveModule call + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, list_find(IGNORED_PTR_ARG, IGNORED_PTR_ARG, fake_module)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, list_item_get_value(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) /*this is the lock protecting mq_lock*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Post(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Join(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(message)); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_remove(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + + ///act + result = MessageBus_RemoveModule(bus, fake_module); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_OK); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_108: [If bus is NULL then MessageBus_IncRef shall do nothing.] +TEST_FUNCTION(MessageBus_IncRef_does_nothing_with_null_input) +{ + ///arrange + CMessageBusMocks mocks; + + ///act + MessageBus_IncRef(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_109: [Otherwise, `MessageBus_IncRef` shall increment the internal ref count.] +TEST_FUNCTION(MessageBus_IncRef_increments_ref_count_1) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + ///act + MessageBus_IncRef(bus); + MessageBus_DecRef(bus); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_111: [ Otherwise, MessageBus_Destroy shall decrement the internal ref count of the message. ] +TEST_FUNCTION(MessageBus_IncRef_increments_ref_count_1_destroy) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + ///act + MessageBus_IncRef(bus); + MessageBus_Destroy(bus); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_058: [If bus is NULL the function shall do nothing.] +TEST_FUNCTION(MessageBus_Destroy_does_nothing_with_null_input) +{ + ///arrange + CMessageBusMocks mocks; + + ///act + MessageBus_Destroy(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_058: [If bus is NULL the function shall do nothing.] +TEST_FUNCTION(MessageBus_DecRef_does_nothing_with_null_input) +{ + ///arrange + CMessageBusMocks mocks; + + ///act + MessageBus_DecRef(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_112: [If the ref count is zero then the allocated resources are freed.] +TEST_FUNCTION(MessageBus_Destroy_works) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + // these are for MessageBus_Destroy + STRICT_EXPECTED_CALL(mocks, Lock_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_head_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + MessageBus_Destroy(bus); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_112: [If the ref count is zero then the allocated resources are freed.] +//Tests_SRS_MESSAGE_BUS_13_113: [ This function shall implement all the requirements of the MessageBus_Destroy API. ] +TEST_FUNCTION(MessageBus_DecRef_works) +{ + ///arrange + CMessageBusMocks mocks; + auto bus = MessageBus_Create(); + mocks.ResetAllCalls(); + + // these are for MessageBus_Destroy + STRICT_EXPECTED_CALL(mocks, Lock_Deinit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_head_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + MessageBus_DecRef(bus); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_030: [If bus or message is NULL the function shall return MESSAGE_BUS_INVALIDARG.] +TEST_FUNCTION(MessageBus_Publish_fails_with_null_bus) +{ + ///arrange + CMessageBusMocks mocks; + + ///act + auto r1 = MessageBus_Publish(NULL, (MESSAGE_HANDLE)0x1); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, r1, MESSAGE_BUS_INVALIDARG); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_030: [If bus or message is NULL the function shall return MESSAGE_BUS_INVALIDARG.] +TEST_FUNCTION(MessageBus_Publish_fails_with_null_message) +{ + ///arrange + CMessageBusMocks mocks; + + ///act + auto r1 = MessageBus_Publish((MESSAGE_BUS_HANDLE)0x1, NULL); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, r1, MESSAGE_BUS_INVALIDARG); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_MESSAGE_BUS_13_037: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_Publish_fails_when_lock_on_modules_lock_fails) +{ + ///arrange + CMessageBusMocks mocks; + + auto bus = MessageBus_Create(); + + // create a message to send + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake }; + auto message = Message_Create(&c); + + mocks.ResetAllCalls(); + + // this is for MessageBus_Publish + whenShallLock_fail = 1; + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto result = MessageBus_Publish(bus, message); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(message); + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_037: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_Publish_fails_when_lock_on_module_mq_lock_fails) +{ + ///arrange + CMessageBusMocks mocks; + + auto bus = MessageBus_Create(); + + // create a message to send + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake }; + auto message = Message_Create(&c); + + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + mocks.ResetAllCalls(); + + // this is for MessageBus_Publish + whenShallLock_fail = currentLock_call + 2; + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_head_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_next_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_item_get_value(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + result = MessageBus_Publish(bus, message); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(message); + MessageBus_RemoveModule(bus, fake_module); + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_037: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_Publish_fails_when_vector_push_back_fails) +{ + ///arrange + CMessageBusMocks mocks; + + auto bus = MessageBus_Create(); + + // create a message to send + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake }; + auto message = Message_Create(&c); + + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + mocks.ResetAllCalls(); + + // this is for MessageBus_Publish + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_head_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_next_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_item_get_value(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + whenShallVECTOR_push_back_fail = currentVECTOR_push_back_call + 1; + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, Message_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + result = MessageBus_Publish(bus, message); + + ///assert + ASSERT_ARE_EQUAL(int, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(message); + MessageBus_RemoveModule(bus, fake_module); + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_037: [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_Publish_fails_when_vector_condition_post_fails) +{ + ///arrange + CMessageBusMocks mocks; + + auto bus = MessageBus_Create(); + + // create a message to send + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake }; + auto message = Message_Create(&c); + + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + mocks.ResetAllCalls(); + + // this is for MessageBus_Publish + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_head_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_next_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_item_get_value(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, Message_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + whenShallCond_Post_fail = 1; + STRICT_EXPECTED_CALL(mocks, Condition_Post(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + result = MessageBus_Publish(bus, message); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_ERROR); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(message); + MessageBus_RemoveModule(bus, fake_module); + MessageBus_Destroy(bus); +} + +//Tests_SRS_MESSAGE_BUS_13_031: [MessageBus_Publish shall acquire the lock MESSAGE_BUS_HANDLE_DATA::modules_lock.] +//Tests_SRS_MESSAGE_BUS_13_032: [ MessageBus_Publish shall start a processing loop for every module in MESSAGE_BUS_HANDLE_DATA::modules. ] +//Tests_SRS_MESSAGE_BUS_13_033 : [In the loop, the function shall first acquire the lock on MESSAGE_BUS_MODULEINFO::mq_lock.] +//Tests_SRS_MESSAGE_BUS_13_034 : [The function shall then append message to MESSAGE_BUS_MODULEINFO::mq by calling Message_Clone and VECTOR_push_back.] +//Tests_SRS_MESSAGE_BUS_13_035 : [The function shall then release MESSAGE_BUS_MODULEINFO::mq_lock.] +//Tests_SRS_MESSAGE_BUS_13_096 : [The function shall then signal MESSAGE_BUS_MODULEINFO::mq_cond.] +//Tests_SRS_MESSAGE_BUS_13_040 : [MessageBus_Publish shall release the lock MESSAGE_BUS_HANDLE_DATA::modules_lock after the loop.] +//Tests_SRS_MESSAGE_BUS_13_037 : [This function shall return MESSAGE_BUS_ERROR if an underlying API call to the platform causes an error or MESSAGE_BUS_OK otherwise.] +TEST_FUNCTION(MessageBus_Publish_succeeds) +{ + ///arrange + CMessageBusMocks mocks; + + auto bus = MessageBus_Create(); + + // create a message to send + unsigned char fake; + MESSAGE_CONFIG c = { 1, &fake, (MAP_HANDLE)&fake }; + auto message = Message_Create(&c); + + auto result = MessageBus_AddModule(bus, fake_module, &fake_module_apis); + + mocks.ResetAllCalls(); + + // this is for MessageBus_Publish + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Lock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Unlock(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_head_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_get_next_item(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, list_item_get_value(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, Message_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Condition_Post(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + result = MessageBus_Publish(bus, message); + + ///assert + ASSERT_ARE_EQUAL(MESSAGE_BUS_RESULT, result, MESSAGE_BUS_OK); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Message_Destroy(message); + MessageBus_RemoveModule(bus, fake_module); + MessageBus_Destroy(bus); +} + +END_TEST_SUITE(message_bus_unittests) diff --git a/core/tests/module_loader_unittests/CMakeLists.txt b/core/tests/module_loader_unittests/CMakeLists.txt new file mode 100644 index 00000000..1aac0ec7 --- /dev/null +++ b/core/tests/module_loader_unittests/CMakeLists.txt @@ -0,0 +1,25 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for module_loader_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(testSuiteName module_loader_unittests) +set(${testSuiteName}_cpp_files +${testSuiteName}.cpp +) + +set(${testSuiteName}_c_files +../../src/module_loader.c +) + +set(${testSuiteName}_h_files +../../inc/module_loader.h +../../inc/dynamic_library.h + +) + +include_directories(${GW_INC}) + +build_test_artifacts(${testSuiteName} ON) \ No newline at end of file diff --git a/core/tests/module_loader_unittests/main.c b/core/tests/module_loader_unittests/main.c new file mode 100644 index 00000000..10d8e294 --- /dev/null +++ b/core/tests/module_loader_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(module_loader_unittests, failedTestCount); + return failedTestCount; +} diff --git a/core/tests/module_loader_unittests/module_loader_unittests.cpp b/core/tests/module_loader_unittests/module_loader_unittests.cpp new file mode 100644 index 00000000..91d3c055 --- /dev/null +++ b/core/tests/module_loader_unittests/module_loader_unittests.cpp @@ -0,0 +1,428 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +#include "module_loader.h" +#include "dynamic_library.h" + +#include "azure_c_shared_utility/lock.h" + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ + +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit + + +}; + +static size_t currentmalloc_call; +static size_t whenShallmalloc_fail; + +// Value for a good handle +#define TEST_MODULE_LIBRARY_GOOD_HANDLE (void*)0xDEAF +// Value for a valid file handle to a file that does not have a valid symbol +#define TEST_MODULE_LIBRARY_BADSYM_HANDLE (void*)0xDEDE +// Value for good library; +#define TEST_MODULE_LIBRARY_GOOD_NAME ("good") +// Value for a valid library but does not have a valid symbol. +#define TEST_MODULE_LIBRARY_BAD_SYM_NAME ("badsym") +// Value for a library that does not load. +#define TEST_MODULE_LIBRARY_BAD_NAME ("bad") + + +static bool test_getApi_func_success = true; + +TYPED_MOCK_CLASS(CModuleLoaderMocks, CGlobalMock) +{ +public: + //extern void* DynamicLibrary_LoadLibrary(const char* dynamicLibraryFileName); + MOCK_STATIC_METHOD_1(, void*, DynamicLibrary_LoadLibrary, const char*, dynamicLibraryFileName) + void* result1; + if (strcmp(dynamicLibraryFileName, TEST_MODULE_LIBRARY_BAD_NAME) == 0) + { + result1 = NULL; + } + else + { + result1 = malloc(sizeof(void*)); + if (strcmp(dynamicLibraryFileName, TEST_MODULE_LIBRARY_BAD_SYM_NAME) == 0) + { + *(void**)result1 = TEST_MODULE_LIBRARY_BADSYM_HANDLE; + } + else if (strcmp(dynamicLibraryFileName, TEST_MODULE_LIBRARY_GOOD_NAME) == 0) + { + *(void**)result1 = TEST_MODULE_LIBRARY_GOOD_HANDLE; + } + } + MOCK_METHOD_END(void*, result1) + + //extern void DynamicLibrary_UnloadLibrary(void* library); + MOCK_STATIC_METHOD_1(, void, DynamicLibrary_UnloadLibrary, void*, library) + if ((library != NULL) && + (library != TEST_MODULE_LIBRARY_BADSYM_HANDLE) && + (library != TEST_MODULE_LIBRARY_GOOD_HANDLE)) + { + free(library); + } + MOCK_VOID_METHOD_END() + + //extern void* DynamicLibrary_FindSymbol(void* library, const char* symbolName); + MOCK_STATIC_METHOD_2(, void*, DynamicLibrary_FindSymbol, void*, library, const char*, symbolName) + void * result2 = (void*)&test_getApi_func; + if ((library != NULL) && + (library != TEST_MODULE_LIBRARY_BADSYM_HANDLE) && + (library != TEST_MODULE_LIBRARY_GOOD_HANDLE)) + { + if (*(void**)library == TEST_MODULE_LIBRARY_BADSYM_HANDLE) + { + result2 = NULL; + } + } + if (library == TEST_MODULE_LIBRARY_BADSYM_HANDLE) + { + result2 = NULL; + } + MOCK_METHOD_END(void*, result2) + + // Mock GetAPIS function, returned on successful call of FindSymbol. + MOCK_STATIC_METHOD_0(,void *, test_getApi_func) + void * result3 = TEST_MODULE_LIBRARY_GOOD_HANDLE; + if (test_getApi_func_success != true) + { + result3 = NULL; + } + MOCK_METHOD_END(void*, result3) + + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2; + currentmalloc_call++; + if (whenShallmalloc_fail>0) + { + if (currentmalloc_call == whenShallmalloc_fail) + { + result2 = (void*)NULL; + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + } + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + } + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_2(, void*, gballoc_realloc, void*, ptr, size_t, size) + MOCK_METHOD_END(void*, BASEIMPLEMENTATION::gballoc_realloc(ptr, size)); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + +}; +DECLARE_GLOBAL_MOCK_METHOD_1(CModuleLoaderMocks, , void*, DynamicLibrary_LoadLibrary, const char*, dynamicLibraryFileName); +DECLARE_GLOBAL_MOCK_METHOD_1(CModuleLoaderMocks, , void, DynamicLibrary_UnloadLibrary, void*, library); +DECLARE_GLOBAL_MOCK_METHOD_2(CModuleLoaderMocks, , void*, DynamicLibrary_FindSymbol, void*, library, const char*, symbolName); +DECLARE_GLOBAL_MOCK_METHOD_0(CModuleLoaderMocks, , void*, test_getApi_func); + +DECLARE_GLOBAL_MOCK_METHOD_1(CModuleLoaderMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_2(CModuleLoaderMocks, , void*, gballoc_realloc, void*, ptr, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CModuleLoaderMocks, , void, gballoc_free, void*, ptr) + +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; +static MICROMOCK_MUTEX_HANDLE g_testByTest; + +BEGIN_TEST_SUITE(module_loader_unittests) + + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + currentmalloc_call = 0; + whenShallmalloc_fail = 0; + test_getApi_func_success = true; + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + currentmalloc_call = 0; + whenShallmalloc_fail = 0; + test_getApi_func_success = true; + } + + /*Tests_SRS_MODULE_LOADER_17_001: [ModuleLoader_Load shall validate the moduleFileName, if it is NULL or an empty string, it will return NULL.]*/ + TEST_FUNCTION(ModuleLoader_Load_Name_Is_Null) + { + CModuleLoaderMocks mocks; + ///arrange + const char* moduleFileName = NULL; + + ///act + MODULE_LIBRARY_HANDLE moduleHandle = ModuleLoader_Load(moduleFileName); + + ///assert + ASSERT_IS_NULL(moduleHandle); + + ///cleanup + } + + + /*Tests_SRS_MODULE_LOADER_17_014: [If memory allocation is not successful, the load shall fail, and it shall return NULL.]*/ + TEST_FUNCTION(ModuleLoader_Load_Malloc_Fails) + { + CModuleLoaderMocks mocks; + ///arrange + const char* moduleFileName = TEST_MODULE_LIBRARY_GOOD_NAME; + + whenShallmalloc_fail = 1; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + ///act + MODULE_LIBRARY_HANDLE moduleHandle = ModuleLoader_Load(moduleFileName); + + ///assert + ASSERT_IS_NULL(moduleHandle); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MODULE_LOADER_17_012: [If load library is not successful, the load shall fail, and it shall return NULL.]*/ + TEST_FUNCTION(ModuleLoader_Load_Library_Fails) + { + CModuleLoaderMocks mocks; + ///arrange + const char* moduleFileName = TEST_MODULE_LIBRARY_BAD_NAME; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_LoadLibrary(moduleFileName)); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + MODULE_LIBRARY_HANDLE moduleHandle = ModuleLoader_Load(moduleFileName); + + ///assert + ASSERT_IS_NULL(moduleHandle); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_MODULE_LOADER_17_013: [If locating the function is not successful, the load shall fail, and it shall return NULL.]*/ + TEST_FUNCTION(ModuleLoader_Load_Symbol_Fails) + { + CModuleLoaderMocks mocks; + ///arrange + const char* moduleFileName = TEST_MODULE_LIBRARY_BAD_SYM_NAME; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_LoadLibrary(moduleFileName)); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_FindSymbol(IGNORED_PTR_ARG, MODULE_GETAPIS_NAME)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_UnloadLibrary(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + MODULE_LIBRARY_HANDLE moduleHandle = ModuleLoader_Load(moduleFileName); + + ///assert + ASSERT_IS_NULL(moduleHandle); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + + /* Tests_SRS_MODULE_LOADER_17_015: [If the get API call returns NULL, the load shall fail, and it shall return NULL.] */ + TEST_FUNCTION(ModuleLoader_Load_GetAPIs_Is_Null) + { + CModuleLoaderMocks mocks; + ///arrange + test_getApi_func_success = false; + const char* moduleFileName = TEST_MODULE_LIBRARY_GOOD_NAME; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_LoadLibrary(moduleFileName)); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_FindSymbol(IGNORED_PTR_ARG, MODULE_GETAPIS_NAME)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, test_getApi_func()); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_UnloadLibrary(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + MODULE_LIBRARY_HANDLE moduleHandle = ModuleLoader_Load(moduleFileName); + + ///assert + ASSERT_IS_NULL(moduleHandle); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + /* Tests_SRS_MODULE_LOADER_17_002: [ModuleLoader_Load shall load the library as a file, the filename given by the moduleLibraryFileName.] */ + /* Tests_SRS_MODULE_LOADER_17_003: [ModuleLoader_Load shall locate the function defined by MODULE_GETAPIS_NAME in the open library.] */ + /* Tests_SRS_MODULE_LOADER_17_004: [ModuleLoader_Load shall call the function defined by MODULE_GETAPIS_NAME in the open library.]*/ + /* Tests_SRS_MODULE_LOADER_17_005: [ModuleLoader_Load shall allocate memory for the structure MODULE_LIBRARY_HANDLE.] */ + /* Tests_SRS_MODULE_LOADER_17_006: [ModuleLoader_Load shall return a non-NULL handle to a MODULE_LIBRARY_DATA_TAG upon success.]*/ + TEST_FUNCTION(ModuleLoader_Success) + { + CModuleLoaderMocks mocks; + ///arrange + const char* moduleFileName = TEST_MODULE_LIBRARY_GOOD_NAME; + test_getApi_func_success = true; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_LoadLibrary(moduleFileName)); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_FindSymbol(IGNORED_PTR_ARG, MODULE_GETAPIS_NAME)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, test_getApi_func()); + + ///act + MODULE_LIBRARY_HANDLE moduleHandle = ModuleLoader_Load(moduleFileName); + + ///assert + ASSERT_ARE_NOT_EQUAL(void_ptr, NULL, moduleHandle); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + ModuleLoader_Unload(moduleHandle); + } + + /*Tests_SRS_MODULE_LOADER_17_007: [ModuleLoader_GetModuleAPIs shall return NULL if the moduleLibrary is NULL.]*/ + TEST_FUNCTION(ModuleLoader_GetModuleAPIs_Library_Is_Null) + { + CModuleLoaderMocks mocks; + ///arrange + MODULE_LIBRARY_HANDLE moduleLibrary = NULL; + + ///act + const MODULE_APIS* apisHandle = ModuleLoader_GetModuleAPIs(moduleLibrary); + + ///assert + ASSERT_IS_NULL(apisHandle); + + ///cleanup + } + + /*Tests_SRS_MODULE_LOADER_17_008: [ModuleLoader_GetModuleAPIs shall return a valid pointer to MODULE_APIS on success.]*/ + TEST_FUNCTION(ModuleLoader_GetModuleAPIs_Name_Is_Good) + { + CModuleLoaderMocks mocks; + ///arrange + const char* moduleFileName = TEST_MODULE_LIBRARY_GOOD_NAME; + test_getApi_func_success = true; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_LoadLibrary(moduleFileName)); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_FindSymbol(IGNORED_PTR_ARG, MODULE_GETAPIS_NAME)); + STRICT_EXPECTED_CALL(mocks, test_getApi_func()); + MODULE_LIBRARY_HANDLE moduleHandle = ModuleLoader_Load(moduleFileName); + ASSERT_IS_NOT_NULL(moduleHandle); + mocks.ResetAllCalls(); + + ///act + const MODULE_APIS* apisHandle = ModuleLoader_GetModuleAPIs(moduleHandle ); + + ///assert + ASSERT_ARE_EQUAL(void_ptr, TEST_MODULE_LIBRARY_GOOD_HANDLE, apisHandle); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + ModuleLoader_Unload(moduleHandle); + + } + + /*Tests_SRS_MODULE_LOADER_17_009: [ModuleLoader_Unload shall do nothing if the moduleLibrary is NULL.]*/ + TEST_FUNCTION(ModuleLoader_Null_Module) + { + CModuleLoaderMocks mocks; + ///arrange + MODULE_LIBRARY_HANDLE moduleLibrary = NULL; + + ///act + ModuleLoader_Unload(moduleLibrary); + + ///assert + + ///cleanup + } + + /*Tests_SRS_MODULE_LOADER_17_010 : [ModuleLoader_Unload shall attempt to unload the library.]*/ + /*Tests_SRS_MODULE_LOADER_17_011 : [ModuleLoader_UnLoad shall deallocate memory for the structure MODULE_LIBRARY_HANDLE.]*/ + TEST_FUNCTION(ModuleLoader_UnLoad_Success) + { + CModuleLoaderMocks mocks; + + ///arrange + const char* moduleFileName = TEST_MODULE_LIBRARY_GOOD_NAME; + test_getApi_func_success = true; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_LoadLibrary(moduleFileName)); + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_FindSymbol(IGNORED_PTR_ARG, MODULE_GETAPIS_NAME)); + STRICT_EXPECTED_CALL(mocks, test_getApi_func()); + + MODULE_LIBRARY_HANDLE moduleHandle = ModuleLoader_Load(moduleFileName); + ASSERT_IS_NOT_NULL(moduleHandle); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, DynamicLibrary_UnloadLibrary(TEST_MODULE_LIBRARY_GOOD_HANDLE)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + ModuleLoader_Unload(moduleHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + +END_TEST_SUITE(module_loader_unittests) diff --git a/core/valgrind_suppressions.txt b/core/valgrind_suppressions.txt new file mode 100644 index 00000000..66e66cb4 --- /dev/null +++ b/core/valgrind_suppressions.txt @@ -0,0 +1,39 @@ +{ + glib_stuff_dont_care + Memcheck:Leak + match-leak-kinds: possible + ... + fun:g_type_register_fundamental + ... +} +{ + glib_stuff_dont_care2 + Memcheck:Leak + match-leak-kinds: possible + ... + fun:g_type_register_static + ... +} +{ + glib_stuff_dont_care3 + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:g_malloc* + ... + obj:/usr/lib/*/libgobject* + ... + fun:call_init.part.0 + fun:call_init + fun:_dl_init + ... +} +{ + glib_stuff_dont_care4 + Memcheck:Leak + match-leak-kinds: possible + ... + fun:g_type_add_interface_static + ... +} + diff --git a/deps/azure-c-shared-utility b/deps/azure-c-shared-utility new file mode 160000 index 00000000..2da5e294 --- /dev/null +++ b/deps/azure-c-shared-utility @@ -0,0 +1 @@ +Subproject commit 2da5e2943e0fa88578091252c999d2bf6ee6999b diff --git a/deps/azure-iot-sdks b/deps/azure-iot-sdks new file mode 160000 index 00000000..102e72b2 --- /dev/null +++ b/deps/azure-iot-sdks @@ -0,0 +1 @@ +Subproject commit 102e72b20f9cb24f63f5f7cd305e9fd133869122 diff --git a/deps/parson b/deps/parson new file mode 160000 index 00000000..f60ddcd0 --- /dev/null +++ b/deps/parson @@ -0,0 +1 @@ +Subproject commit f60ddcd05b4e5122f727854949244d8b1dc0548e diff --git a/doc/connecting_to_ble_device_on_intel_edison.md b/doc/connecting_to_ble_device_on_intel_edison.md new file mode 100644 index 00000000..78f1a25a --- /dev/null +++ b/doc/connecting_to_ble_device_on_intel_edison.md @@ -0,0 +1,131 @@ +Azure IoT Gateway - Connecting to a Bluetooth Low Energy device on the Intel Edison +=================================================================================== + +Overview +-------- + +As it stands today, the Bluetooth LE (BLE) module that the Azure IoT Gateway SDK +ships with does not support *discovery* of BLE devices. This is however on the +product roadmap. In the meantime, the BLE module is perfectly usable for +prototyping solutions by performing some preparatory work as documented here. + +Prerequisites +-------------- + +Currently, the Azure IoT Gateway SDK supports communicating with BLE devices +only on Linux. Here's what you'll need to do get your Intel Edison board setup: + + 1. Setup your Intel Edison board using instructions as documented for the + [Azure IoT Hub Device SDK](https://github.com/Azure/azure-iot-sdks/blob/master/doc/get_started/yocto-intel-edison-c.md). + You'll need to follow along only up till the section titled "**Installing + Git on your Intel Edison**". + + 2. Upgrade the installation of BlueZ on your board to version 5.37 by building + BlueZ from source. Here're the steps to do so: + + - Stop the currently running bluetooth daemon. + + systemctl stop bluetooth + + - Download and extract the [source code](http://www.kernel.org/pub/linux/bluetooth/bluez-5.37.tar.xz) + for BlueZ version 5.37. + + wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.37.tar.xz + tar -xvf bluez-5.37.tar.xz + cd bluez-5.37 + + - Build and install BlueZ. + + ./configure --disable-udev --disable-systemd --enable-experimental + make + make install + + - Change the *systemd* service configuration for bluetooth so that it + points to the new bluetooth daemon by editing the file + `/lib/systemd/system/bluetooth.service`. Replace the value of the + `ExecStart` attribute so that it looks like this: + + ExecStart=/usr/local/libexec/bluetooth/bluetoothd -E + + 3. Reboot your Edison. + + 4. Unblock bluetooth. + + rfkill unblock bluetooth + + 5. Check that the version of BlueZ is now 5.37. Running the following command + should cause it to print the string `5.37` to the terminal. + + bluetoothctl --version + +Connecting your BLE device +-------------------------- + +Here're the steps to be performed in order to *discover* and connect to a BLE +device: + + 1. Run `bluetoothctl`. You should see output that looks like this: + + root@edison:~# bluetoothctl + [NEW] Controller 98:4F:EE:03:BC:6B edison [default] + [bluetooth]# _ + + 2. You should find yourself inside the interactive bluetooth shell. Start + scanning for BLE devices: + + [bluetooth]# scan on + Discovery started + [CHG] Controller 98:4F:EE:03:BC:6B Discovering: yes + + 2. Now cause your device to enter the mode that will cause it to broadcast + BLE advertisement packets. Eventually you should see your device being + listed by `bluetoothctl`. + + [NEW] Device B0:B4:48:B9:27:82 CC2650 SensorTag + [CHG] Device B0:B4:48:B9:27:82 TxPower: 0 + + 3. Turn off scanning for bluetooth devices: + + [bluetooth]# scan off + [CHG] Device B0:B4:48:B9:27:82 TxPower is nil + [CHG] Device B0:B4:48:B9:27:82 RSSI is nil + [CHG] Controller 98:4F:EE:03:BC:6B Discovering: no + Discovery stopped + [bluetooth]# _ + + 4. Connect to your BLE device: + + [bluetooth]# connect B0:B4:48:B9:27:82 + Attempting to connect to B0:B4:48:B9:27:82 + [CHG] Device B0:B4:48:B9:27:82 Connected: yes + Connection successful + [CC2650 SensorTag]# _ + + 5. After connecting, `bluetoothctl` might proceed to list the GATT + characteristics and attributes supported by your BLE device. Inspect the + characteristics it discovered by running `list-attributes`: + + [CC2650 SensorTag]# list-attributes + Primary Service + /org/bluez/hci0/dev_B0_B4_48_B9_27_82/service000c + Device Information + Characteristic + /org/bluez/hci0/dev_B0_B4_48_B9_27_82/service000c/char000d + System ID + Characteristic + /org/bluez/hci0/dev_B0_B4_48_B9_27_82/service000c/char000f + Model Number String + + The full output has not been shown above - the output has been truncated + after the enumeration of the first 3 GATT attribute rows. + + 6. Now you can proceed to disconnect from the device: + + [CC2650 SensorTag]# disconnect B0:B4:48:B9:27:82 + Attempting to disconnect from B0:B4:48:B9:27:82 + Successful disconnected + [CHG] Device B0:B4:48:B9:27:82 Connected: no + [bluetooth]# _ + +That's it. Now you're fully setup to build and run the BLE gateway module with +your BLE device. \ No newline at end of file diff --git a/doc/devbox_setup.md b/doc/devbox_setup.md new file mode 100644 index 00000000..e81d38be --- /dev/null +++ b/doc/devbox_setup.md @@ -0,0 +1,42 @@ +# Prepare your development environment + +This document describes how to prepare your development environment to use the *Microsoft Azure IoT Gateway SDK*. It describes preparing a development environment in Windows using Visual Studio and in Linux. + +- [Setting up a Windows development environment](#windows) +- [Setting up a Linux development environment](#linux) + + +## Setting up a Windows development environment + +This section shows you how to set up a development environment for the Azure IoT Gateway SDK on Windows 10. + +1. Install [Visual Studio 2015](https://www.visualstudio.com). You can use the free Community Edition if you meet the licensing requirements. +Be sure to include Visual C++ and NuGet Package Manager. +2. Install [git](http://www.git-scm.com) making sure git.exe can be run from a command line. +3. Install [cmake](https://cmake.org/download/) making sure cmake.exe can be run from a command line. + +>Note: Using the .msi is the easiest option when installing on Windows. Add CMake to the PATH for at least the current user when prompted to do so by the installer. +4. Clone the latest version of this repository to your local machine with the recursive parameter +``` +git clone --recursive https://github.com/Azure/azure-iot-gateway-sdk.git +``` +Use the **master** branch to ensure you fetch the latest release version. + +>Note: Make sure to clone the repo into a directory heirachy with less than 20 characters. Windows has limitations on the length of file names and placing the repo in a hierarchy deeper than 20 characters will cause the build to fail. Our `build.cmd` script throws an error if it would eventually hit this failure, but one can hit this when manually building the project with cmake. + +> In the path prefix\azure-iot-gateway-sdk, prefix must be less than 20 characters. + + +## Set up a Linux development environment + +This section shows you how to set up a development environment for the Azure IoT Gateway SDK on Ubuntu. + +1. The following packages are needed and they can be installed with the following command line: +``` +sudo apt-get install curl build-essential libcurl4-openssl-dev git cmake libssl-dev uuid-dev valgrind libglib2.0-dev +``` +2. Clone the latest version of this repository to your Ubuntu machine with the recursive parameter +``` +git clone --recursive https://github.com/Azure/azure-iot-gateway-sdk.git +``` +Use the **master** branch to ensure you fetch the latest release version. \ No newline at end of file diff --git a/doc/getting_started.md b/doc/getting_started.md new file mode 100644 index 00000000..f391d45e --- /dev/null +++ b/doc/getting_started.md @@ -0,0 +1,380 @@ +#Azure IoT Gateway SDK - Getting Started + +This document provides a detailed overview of the Hello World sample [code](../samples/hello_world) which uses the fundamental components of the Azure IoT Gateway SDK architecture to log a hello world message to a file every 5 seconds. + +#The walkthrough covers + +1. **Dev box setup** - steps necessary to build and run the sample +2. **Concepts** - conceptual overview of the components that compose any gateway created with the SDK +3. **Hello World sample architecture** - describes how the concepts apply to the sample and how the components fit together +4. **Detailed architecture** - detailed view of the Hello World sample's architecture +5. **How to build the sample** +6. **How to run the sample** +7. **Typical output** - example of typical output when the hello world sample is run on Linux or Windows +8. **Code snippets** - code snippets which show how and where the Hello World sample implements key gateway components + +##Dev box setup + +A dev box configured with the SDK and necessary libraries is necessary to complete this walkthrough. Please complete the [dev box setup](./devbox_setup.md) before continuing. + +##Concepts + +###Modules + +Modules are the brains of a gateway built with the Azure IoT Gateway SDK. Modules exchange data with each other via messages. A module receives a message, performs some action on it, might transform it into a new message, and subsequently publishes it to other modules. There might be modules that only produce new messages and never process incoming messages. A chain of modules can be thought of as a data processing pipeline with a module being nothing more than a chunk of code performing a transformation on the data at one point in that pipeline. + +![](./media/modules.png) + +The SDK contains the following: + +- prewritten modules which perform common gateway functions +- the interfaces needed for a developer to write his own custom modules +- the infrastructure necessary to deploy and run a set of modules + +The SDK abstracts away operating system dependencies via an abstraction layer in order to allow for gateways to be built on a variety of platforms. + +![](./media/modules_2.png) + +###Messages + +Thinking about modules passing each other messages is a convenient way of conceptualizing how a gateway functions, but it does not accurately reflect what's happening under the hood. Modules actually use a message bus to communicate with each other. They publish messages to the bus and then let the bus broadcast the message to all modules connected to the message bus. + +A module publishes a message to the message bus via the `MessageBus_Publish` function. The message bus delivers messages to a module by invoking a callback on the module and passing that function the message that is being delivered. A message consists of a set of key/value properties and content passed as a block of memory. + +![](./media/messages_1.png) + +At this time, the responsibility of filtering messages falls on each module since the message bus uses a broadcast mechanism (i.e. the message bus delivers each message to all the modules that are connected to the message bus). A module should only act upon a message if the message is intended for it. This message filtering is what effectively creates a message pipeline. This filtering can typically be performed by inspecting the properties on the message that a module receives to determine whether it is of interest to the module or not. + +This is enough background to actually start discussing the Hello World sample. + +##Hello World sample architecture + +Before diving into the details of filtering messages based on properties, first think of the Hello World sample simply in terms of modules. The sample is made up of a pipeline of two modules: +- A "hello world" module +- A logger module + +The "hello world" module creates a message every 5 seconds and passes it to the logger module. The logger module simply writes the message to a file. + +![](./media/high_level_architecture.png) + +##Detailed architecture + +Based on the gateway architecture, we know that the "hello world" module is not directly passing messages to the logger module every 5 seconds. Instead, it is actually publishing a message to the message bus every 5 seconds. + +The logger module is then receiving the message from the message bus and inspecting it to determine if it should act upon it before writing the contents of the message to a file. + +The logger module only consumes messages (by logger them to a file) and never publishes new messages on the bus. + + ![](./media/detailed_architecture.png) + +The figure above shows an accurate representation of the Hello World sample's architecture as well as the relative paths to the source files that implement different portions of the sample. Feel free to explore the code on your own, or use the code snippets below as a guide. + +##How to build the sample +Linux + +1. Open a shell +2. Navigate to `azure-iot-gateway-sdk/tools/` +3. Run `./build.sh` + +>Note: `build.sh` does multiple things. It builds the project and places it in the "build" folder in the root of the repo. This folder is deleted and recreated everytime `build.sh` is run. Additionally, `build.sh` runs all tests. The project can be build manually by using cmake. To do this: + +>create a folder for the build output and navigate to that folder. + +>run `cmake ` + +>run `make -j $(nproc)` + +>To run the tests: + +>run `ctest -j $(nproc) -C Debug --output-on-failure` + +Windows + +1. Open a Developer Command Prompt for VS2015 +2. Navigate to `azure-iot-gateway-sdk\tools\` +3. Run `build.cmd`. + +>Note: `build.cmd` does multiple things. It builds a solution ('azure_iot_gateway_sdk.sln') and places it in the "build" folder in the root of the repo. This folder is deleted and recreated every time `build.cmd` is run. Additionally, `build.cmd` runs all tests. The project can be build manually by using cmake. To do this: + +>create a folder for the build output and navigate to that folder. + +>run `cmake ` + +>run `msbuild /m /p:Configuration="Debug" /p:Platform="Win32" azure_iot_gateway_sdk.sln` + +>To run the tests: + +>run `ctest -C Debug -V` + +##How to run the sample + +Linux + +- build.sh produces its outputs in `azure-iot-gateway-sdk/build`. This is where the two modules used in this sample are built. + +> Note: `liblogger_hl.so`'s relative path is `/modules/logger/liblogger_hl.so` and `libhello_world_dl.so` is built in `/modules/hello_world/libhello_world_hl.so`. Use these paths for the "module path" value in the json below. + +- Copy the JSON file from azure-iot-gateway-sdk/samples/hello_world/src/hello_world_lin.json to the build folder. + +- For the logger_hl module, replace in "args" the "filename" value with the path to the file that will contain the log. +This is an example of a JSON settings file for Linux that will write to `log.txt`, if started from `azure-iot-gateway-sdk/build`. The Hello World sample already has a JSON settings file and you do not need to change it. This example has been provided in case you want to change the output location for the log file. + +```json +{ + "modules" : + [ + { + "module name" : "logger_hl", + "module path" : "./modules/logger/liblogger_hl.so", + "args" : + { + "filename":"log.txt" + } + }, + { + "module name" : "hello_world", + "module path" : "./modules/hello_world/libhello_world_hl.so", + "args" : null + } + ] +} +``` + +- Navigate to `azure-iot-gateway-sdk/build/`. + +- Run `$ ./samples/hello_world/hello_world_sample ` + +>Note: The simulated device cloud upload process takes the path to a JSON configuration file as an argument in the command line. An example JSON file has been provided as part of the repo at `azure-iot-gateway-sdk/samples/hello_world/src/hello_world_lin.json'. + +Windows + +- From a Developer Command for VS2015 run `build.cmd`. `build.cmd` produces a folder called `build` in the root repo folder. This is where the two modules used in this sample are built. + +> Note: 'logger_hl.dll''s relative pathe is 'modules\logger\logger_hl.dll' and 'hello_world_hl.dll' is built in 'modules\hello_world\hello_world_hl.dll'. Use these paths for the "module path" value in the json below. + +- Copy the JSON file from folder: `samples\hello_world\src\hello_world_win.json` to folder: `build\samples\hello_world\Debug\`. + +- For the logger_hl module, replace in `args` the `filename` value with the path to the file that will contain the log. +This is an example of a JSON settings file for Windows. that will write to `log.txt` in your current working directory. +```json +{ + "modules" : + [ + { + "module name" : "logger_hl", + "module path" : "..\\..\\..\\modules\\logger\\Debug\\logger_hl.dll", + "args" : + { + "filename":"log.txt" + } + }, + { + "module name" : "hello_world", + "module path" : "..\\..\\..\\modules\\hello_world\\Debug\\hello_world_hl.dll", + "args" : null + } + ] +} +``` + +- Navigate to `build\samples\hello_world\Debug\`. + +- Run `.\hello_world_sample.exe ` + +>Note: The simulated device cloud upload process takes the path to a JSON configuration file as an argument in the command line. An example JSON file has been provided as part of the repo at `azure-iot-gateway-sdk\samples\hello_world\src\hello_world_win.json'. + + +## Typical Output +Below is an example of typical output that is written to the log file when the Hello World sample is run on Linux or Windows. The output below has been formatted for readability purposes. + +```json +[{ + "time": "Mon Apr 11 13:48:07 2016", + "content": "Log started" +}, { + "time": "Mon Apr 11 13:48:48 2016", + "properties": { + "helloWorld": "from Azure IoT Gateway SDK simple sample!" + }, + "content": "aGVsbG8gd29ybGQ=" +}, { + "time": "Mon Apr 11 13:48:55 2016", + "properties": { + "helloWorld": "from Azure IoT Gateway SDK simple sample!" + }, + "content": "aGVsbG8gd29ybGQ=" +}, { + "time": "Mon Apr 11 13:49:01 2016", + "properties": { + "helloWorld": "from Azure IoT Gateway SDK simple sample!" + }, + "content": "aGVsbG8gd29ybGQ=" +}, { + "time": "Mon Apr 11 13:49:04 2016", + "content": "Log stopped" +}] +``` + +##Code snippets + +###Gateway creation + +The gateway process needs to be written by the developer. This a program which creates internal infrastructure (e.g. the message bus), loads the correct modules, and sets everything up to function correctly. The SDK provides the `Gateway_Create_From_JSON` function which allows developers to bootstrap a gateway from a JSON file. + +`Gateway_Create_FromJSON` deals with creating internal infrastructure (e.g. the message bus), loading modules, and setting everything up to function correctly. All the developer needs to do is provide this function with the path to a JSON file specifying what modules they want loaded. + +The code for the Hello World sample's gateway process is contained in [`samples/hello_world/main.c`](../samples/hello_world/src/main.c) A slightly abbreviated version of that code is copied below. This very short program just creates a gateway and then waits for the ENTER key to be pressed before it tears down the gateway. + +```c +int main(int argc, char** argv) +{ + GATEWAY_HANDLE gateway; + if ((gateway = Gateway_Create_From_JSON(argv[1])) == NULL) + { + printf("failed to create the gateway from JSON\n"); + } + else + { + printf("gateway successfully created from JSON\n"); + printf("gateway shall run until ENTER is pressed\n"); + (void)getchar(); + Gateway_LL_Destroy(gateway); + } + return 0; +} +``` + +The JSON file specifying the modules to be loaded is quite simple. It contains a list of modules to load. Each module must specify a: +- `module name` – a unique name for the module +- `module path` – the path to the library containing the module. For Linux this will be a .so while on Windows this will be a .dll file +- `args` – any arguments/configuration the module needs. Specificaly, they are really another json value which is passed (as string) to the Module's `_Create` function. + +Copied below is the code from one of the JSON files (hello_world_lin.json)[(../samples/hello_world/src/hello_world_lin.json) used to configure the Hello World sample. There are a couple things to note: + +- This is the Linux version of the file +- Whether a module takes an argument or not is completely dependent on the module's design. The logger module does take an argument which is the path to the file to be used for output. On the other hand, the "hello world" module does not take any arguments. + +```json +{ + "modules" : + [ + { + "module name" : "logger_hl", + "module path" : "./modules/logger/liblogger_hl.so", + "args" : {"filename":"log.txt"} + }, + { + "module name" : "hello_world", + "module path" : "./modules/hello_world/libhello_world_hl.so", + "args" : null + } + ] +} +``` + +###"hello world" module message publishing + +The code used by the "hello world" module to publish messages is in ['hello_world.c'](../modules/hello_world/src/hello_world.c). An amended version has been reproduced below. Comments calling out the more interesting parts of message creation and publishing process has been given below. +Also, error checks have been omitted for the sake of brevity. + +```c +int helloWorldThread(void *param) +{ + // create data structures used in function. + HELLOWORLD_HANDLE_DATA* handleData = param; + MESSAGE_CONFIG msgConfig; + MAP_HANDLE propertiesMap = Map_Create(NULL); + + // add a property named "helloWorld" with a value of "from Azure IoT + // Gateway SDK simple sample!" to a set of message properties that + // will be appended to the message before publishing it. + Map_AddOrUpdate(propertiesMap, "helloWorld", "from Azure IoT Gateway SDK simple sample!") + + // set the content for the message + msgConfig.size = strlen(HELLOWORLD_MESSAGE); + msgConfig.source = HELLOWORLD_MESSAGE; + + // set the properties for the message + msgConfig.sourceProperties = propertiesMap; + + // create a message based on the msgConfig structure + MESSAGE_HANDLE helloWorldMessage = Message_Create(&msgConfig); + + while (1) + { + if (handleData->stopThread) + { + (void)Unlock(handleData->lockHandle); + break; /*gets out of the thread*/ + } + else + { + // publish the message to the bus + (void)MessageBus_Publish(handleData->busHandle, helloWorldMessage); + (void)Unlock(handleData->lockHandle); + } + + (void)ThreadAPI_Sleep(5000); /*every 5 seconds*/ + } + + Message_Destroy(helloWorldMessage); + + return 0; +} +``` + +###"hello world" module message processing + +The "hello world" module does not ever have to process any messages since it is not interested in any of the messages that are published to the message bus. This makes implementation of the "hello world" module's message receive callback a no-op function. + +```c +static void HelloWorld_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + /*no action, HelloWorld is not interested in any messages*/ +} +``` + +###Logger module message publishing + +The logger module receives messages from the message bus and writes them to a file. It never has to publish messages to the message bus. Therefore, the code of the logger module never calls 'MessageBus_Publish'. + +`Logger_Recieve`, located in [`logger.c`](../modules/logger/src/logger.c), is the logger module's callback invoked by the message bus when it wants to deliver a message to the logger module. An amended version has been reproduced below. Comments call out parts of message processing have been provided. Again, error checks have been omitted for the sake of brevity. +```c +static void Logger_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + + time_t temp = time(NULL); + struct tm* t = localtime(&temp); + char timetemp[80] = { 0 }; + + // get the message properties from the message + CONSTMAP_HANDLE originalProperties = Message_GetProperties(messageHandle); + MAP_HANDLE propertiesAsMap = ConstMap_CloneWriteable(originalProperties); + + // convert the collection of properties into a JSON string + STRING_HANDLE jsonProperties = Map_ToJSON(propertiesAsMap); + + // base64 encode the message content + const CONSTBUFFER * content = Message_GetContent(messageHandle); + STRING_HANDLE contentAsJSON = Base64_Encode_Bytes(content->buffer, content->size); + + // start the construction of the final string to be logged by adding + // the timestamp + STRING_HANDLE jsonToBeAppended = STRING_construct(",{\"time\":\""); + STRING_concat(jsonToBeAppended, timetemp); + + // add the message properties + STRING_concat(jsonToBeAppended, "\",\"properties\":"); + STRING_concat_with_STRING(jsonToBeAppended, jsonProperties); + + // add the content + STRING_concat(jsonToBeAppended, ",\"content\":\""); + STRING_concat_with_STRING(jsonToBeAppended, contentAsJSON); + STRING_concat(jsonToBeAppended, "\"}]"); + + // write the formatted string + LOGGER_HANDLE_DATA *handleData = (LOGGER_HANDLE_DATA *)moduleHandle; + addJSONString(handleData->fout, STRING_c_str(jsonToBeAppended); +} +``` + diff --git a/doc/media/detailed_architecture.png b/doc/media/detailed_architecture.png new file mode 100644 index 00000000..6e53ea9e Binary files /dev/null and b/doc/media/detailed_architecture.png differ diff --git a/doc/media/gateway_ble_command_data_flow.png b/doc/media/gateway_ble_command_data_flow.png new file mode 100644 index 00000000..09b5112b Binary files /dev/null and b/doc/media/gateway_ble_command_data_flow.png differ diff --git a/doc/media/gateway_ble_upload_data_flow.png b/doc/media/gateway_ble_upload_data_flow.png new file mode 100644 index 00000000..4c709fff Binary files /dev/null and b/doc/media/gateway_ble_upload_data_flow.png differ diff --git a/doc/media/high_level_architecture.png b/doc/media/high_level_architecture.png new file mode 100644 index 00000000..be8a6f89 Binary files /dev/null and b/doc/media/high_level_architecture.png differ diff --git a/doc/media/messages_1.png b/doc/media/messages_1.png new file mode 100644 index 00000000..df939808 Binary files /dev/null and b/doc/media/messages_1.png differ diff --git a/doc/media/modules.png b/doc/media/modules.png new file mode 100644 index 00000000..571c9619 Binary files /dev/null and b/doc/media/modules.png differ diff --git a/doc/media/modules_2.png b/doc/media/modules_2.png new file mode 100644 index 00000000..6362ee6e Binary files /dev/null and b/doc/media/modules_2.png differ diff --git a/doc/sample_ble.md b/doc/sample_ble.md new file mode 100644 index 00000000..41f36ef8 --- /dev/null +++ b/doc/sample_ble.md @@ -0,0 +1,248 @@ +Bluetooth Low Energy Telemetry Sample for Azure IoT Gateway SDK +=============================================================== + +Overview +-------- + +This sample showcases how one might build an IoT Gateway that interacts with a +Bluetooth Low Energy (BLE) device using the Azure IoT Gateway SDK. The sample +contains the following modules: + + 1. A Bluetooth Low Energy (BLE) module that interfaces with the BLE device to + read temperature data. + 2. A logger module for producing message bus diagnostics. + 3. An identity mapping module for translating between BLE device MAC addresses + and Azure IoT Hub device identities. + 4. An IoT Hub HTTP module for uploading BLE telemetry data and for receiving + device commands from the Azure IoT Hub. + 5. A BLE Printer module that interprets telemetry from the Texas Instruments + SensorTag device and prints formatted data to the console. + +How does the data flow through the Gateway +------------------------------------------ + +The telemetry upload data flow pipeline is best described via a block diagram: + +![](./media/gateway_ble_upload_data_flow.png) + +Here's the journey that a piece of telemetry data takes originating from a BLE +device before finding its way to an Azure IoT Hub. + + 1. The BLE device generates a temperature sample and transfers it over + Bluetooth to the BLE module. + 2. The BLE Module receives the sample and publishes it on to the message bus + along with the device's MAC address. + 3. The identity mapping module picks up this message from the message bus and + looks up its internal table in order to translate the device MAC address + into an Azure IoT Hub identity (comprised of a device ID and device key). + It then proceeds to publish a new message on to the message bus containing + the temperature sample data, the MAC address, the IoT Hub device ID and + key. + 4. The IoT Hub HTTP module then receives this message from the identity + mapping module and publishes it to the Azure IoT Hub itself. + 5. The logger module logs all messages from the message bus into a file on + the disk. + +The cloud to device command data flow pipeline is described via a block diagram +below: + +![](./media/gateway_ble_command_data_flow.png) + + 1. The IoT Hub HTTP module periodically polls Azure IoT Hub for new command + messages that might be available. + 2. When the IoT Hub HTTP module receives a new command message, it publishes + it on to the message bus. + 3. The Identity Mapping module picks up the message and translates the Azure + IoT Hub device ID to a device MAC address and publishes a new message to + the message bus including the MAC address in the message's properties map. + 4. The BLE module then picks up this message and executes the I/O instruction + by communicating with the BLE device. + +Building the sample +------------------- + +At this point, gateways containing BLE modules are only supported on Linux. The +sample gets built when you build the SDK by running `tools/build.sh`. The +[devbox setup](devbox_setup.md) guide has information on how you can build the +SDK. + +Preparing your BLE device +------------------------- + +This sample has been tested with the [Texas Instruments SensorTag](http://www.ti.com/ww/en/wireless_connectivity/sensortag2015/index.html) +device on an Intel Edison board. Before running the sample you'll want to +prepare the device by following the instructions in the article that talks about +[connecting a BLE device on the Intel Edison](./connecting_to_ble_device_on_intel_edison.md). +Though the article talks about Intel Edison in particular it should be possible +to adapt the instructions to any arbitrary device that supports BLE and +runs Linux. + +Running the sample +------------------ + +In order to bootstrap and run the sample, you'll need to configure each module +that participates in the gateway. This configuration is provided as JSON. All +5 participating modules will need to be configured. There is a sample JSON file +provided in the repo called `gateway_sample.json` which you can use as a +starting point for building your own configuration file. You should find the +file at the path `samples/ble_gateway_hl/src` relative to the root of the repo. + +In order to run the sample you'll run the `ble_gateway_hl` binary passing the +path to the configuration JSON file. + +``` +ble_gateway_hl <> +``` + +Template configuration JSONs are given below for all the modules that are a part +of this sample. The sample configuration for the BLE device assumes a Texas +Instruments SensorTag device. But any standard BLE device that can operate as a +GATT peripheral should work. You'll only need to update the GATT characterstic +IDs and data (for write instructions). + +### Logger configuration + +```json +{ + "module name": "logger", + "module path": "<>", + "args": + { + "filename":"/path/to/log-file-name.log" + } +} +``` + +### BLE module configuration + +```json +{ + "module name": "BLE Device", + "module path": "<>", + "args": { + "controller_index": 0, + "device_mac_address": "<>", + "instructions": [ + { + "type": "read_once", + "characteristic_uuid": "00002A24-0000-1000-8000-00805F9B34FB" + }, + { + "type": "read_once", + "characteristic_uuid": "00002A25-0000-1000-8000-00805F9B34FB" + }, + { + "type": "read_once", + "characteristic_uuid": "00002A26-0000-1000-8000-00805F9B34FB" + }, + { + "type": "read_once", + "characteristic_uuid": "00002A27-0000-1000-8000-00805F9B34FB" + }, + { + "type": "read_once", + "characteristic_uuid": "00002A28-0000-1000-8000-00805F9B34FB" + }, + { + "type": "read_once", + "characteristic_uuid": "00002A29-0000-1000-8000-00805F9B34FB" + }, + { + "type": "write_at_init", + "characteristic_uuid": "F000AA02-0451-4000-B000-000000000000", + "data": "AQ==" + }, + { + "type": "read_periodic", + "characteristic_uuid": "F000AA01-0451-4000-B000-000000000000", + "interval_in_ms": 1000 + }, + { + "type": "write_at_exit", + "characteristic_uuid": "F000AA02-0451-4000-B000-000000000000", + "data": "AA==" + } + ] + } +} +``` + +### IoT Hub HTTP module + +```json +{ + "module name": "IoTHub", + "module path": "<>", + "args": { + "IoTHubName": "<>", + "IoTHubSuffix": "<>" + } +} +``` + +### Identity mapping module configuration + +```json +{ + "module name": "mapping", + "module path": "<>", + "args": [ + { + "macAddress": "AA:BB:CC:DD:EE:FF", + "deviceId": "<>", + "deviceKey": "<>" + } + ] +} +``` + +### BLE Printer module configuration + +```json +{ + "module name": "BLE Printer", + "module path": "<Note: `build.sh` does multiple things. It builds the project and places it in the "build" folder in the root of the repo. This folder is deleted and recreated every time `build.sh` is run. Additionally, `build.sh` runs all tests. The project can be build manually by using cmake. To do this: + +>create a folder for the build output and navigate to that folder. + +>run `cmake ` + +>run `make -j $(nproc)` + +>To run the tests: + +>run `ctest -j $(nproc) -C Debug --output-on-failure` + +Windows + +1. Open a Developer Command for VS2015 +2. Navigate to `azure-iot-gateway-sdk/tools/` +3. Run `build.cmd` + +>Note: `build.cmd` does multiple things. It builds a solution ('azure_iot_gateway_sdk.sln') and places it in the "build" folder in the root of the repo. This folder is deleted and recreated every time `build.cmd` is run. Additionally, `build.cmd` runs all tests. The project can be build manually by using cmake. To do this: + +>create a folder for the build output and navigate to that folder. + +>run `cmake ` + +>run `msbuild /m /p:Configuration="Debug" /p:Platform="Win32" azure_iot_gateway_sdk.sln` + +>To run the tests: + +>run `ctest -C Debug -V` + +## How to run the sample: +Linux + +1. Navigate to `azure-iot-gateway-sdk/build`. + +2. Run `$ ./samples/simulated_device_cloud_upload/simulated_device_cloud_upload_sample ` + +>Note: The simulated device cloud upload process takes the path to a JSON configuration file as an argument in the command line. An example JSON file has been provided as part of the repo at `azure-iot-gateway-sdk/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_lin.json'. + +Windows + +1. Navigate to `azure-iot-gateway-sdk\build\samples\simulated_device_cloud_upload\Debug`. + +2. Run `.\simulated_device_cloud_upload_sample.exe ` + +>Note: The simulated device cloud upload process takes the path to a JSON configuration file as an argument in the command line. An example JSON file has been provided as part of the repo at `azure-iot-gateway-sdk\samples\simulated_device_cloud_upload\src\simulated_device_cloud_upload_win.json'. + diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt new file mode 100644 index 00000000..52ac67a9 --- /dev/null +++ b/modules/CMakeLists.txt @@ -0,0 +1,20 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) +#this is CMakeLists for the modules + +set(MODULES_DIR ${CMAKE_CURRENT_LIST_DIR} CACHE INTERNAL "Modules include directory" FORCE) + +function(linkModule moduleName) + include_directories(${MODULES_DIR}/${moduleName}/inc) +endfunction(linkModule) + +include_directories(./common) + +add_subdirectory(ble) +add_subdirectory(simulated_device) +add_subdirectory(identitymap) +add_subdirectory(iothubhttp) +add_subdirectory(logger) +add_subdirectory(hello_world) \ No newline at end of file diff --git a/modules/README.md b/modules/README.md new file mode 100644 index 00000000..016d4344 --- /dev/null +++ b/modules/README.md @@ -0,0 +1 @@ +# Modules \ No newline at end of file diff --git a/modules/ble/CMakeLists.txt b/modules/ble/CMakeLists.txt new file mode 100644 index 00000000..fc8060e0 --- /dev/null +++ b/modules/ble/CMakeLists.txt @@ -0,0 +1,131 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +# this is CMakeLists for the ble module +cmake_minimum_required(VERSION 2.8.11) + +if(LINUX) + ################################################ + # Include GIO headers/libs + ################################################ + find_package(PkgConfig REQUIRED) + pkg_search_module(GWGIOUNIX REQUIRED gio-unix-2.0) + + include_directories(${GWGIOUNIX_INCLUDE_DIRS}) + set(LIBS ${GWGIOUNIX_LIBRARIES}) + + ################################################ + # Blue-z dbus generated sources + ################################################ + set(bluez_sources + ./deps/linux/dbus-bluez/src/bluez_characteristic.c + ./deps/linux/dbus-bluez/src/bluez_device.c + ) + set(bluez_headers + ./deps/linux/dbus-bluez/inc/bluez_characteristic.h + ./deps/linux/dbus-bluez/inc/bluez_device.h + ) + include_directories(./deps/linux/dbus-bluez/inc) + + ################################################ + # BLE Module sources + ################################################ + set(ble_sources + ${bluez_sources} + ./src/gio_async_seq.c + ./src/ble_gatt_io_linux.c + ./src/ble_gatt_io_linux_connect.c + ./src/ble_gatt_io_linux_disconnect.c + ./src/ble_gatt_io_linux_read.c + ./src/ble_gatt_io_linux_write.c + ./src/bleio_seq_linux.c + ./src/bleio_seq_linux_schedule_write.c + ./src/bleio_seq_linux_schedule_read.c + ./src/bleio_seq_linux_schedule_periodic.c + ./src/ble_utils.c + ./src/ble.c + ) + set(ble_headers + ${bluez_headers} + ./inc/gio_async_seq.h + ./inc/bleio_seq.h + ./inc/ble_gatt_io_linux_common.h + ./inc/bleio_seq_linux_common.h + ./inc/ble.h + ) +elseif(WIN32) + set(ble_sources + ./src/ble_gatt_io_windows.c + ./src/bleio_seq_windows.c + ./src/ble_utils.c + ./src/ble.c + ) +endif() + +set(ble_headers + ${ble_headers} + ./inc/ble_gatt_io.h + ./inc/ble_utils.h + ../common/messageproperties.h +) + +set(ble_static_sources + ${ble_sources} +) + +set(ble_static_headers + ${ble_headers} +) + +set(ble_hl_sources + ./src/ble_hl.c +) + +set(ble_hl_headers + ./inc/ble_hl.h +) + +set(ble_hl_static_sources + ${ble_hl_sources} +) + +set(ble_hl_static_headers + ${ble_hl_headers} +) + +include_directories( + ./inc + ../common + ${GW_INC} +) +set(LIBS ${LIBS} gateway) + +# build ble module as a dynamic library +add_library(ble MODULE ${ble_sources} ${ble_headers}) +target_link_libraries(ble ${LIBS}) +linkSharedUtil(ble) + +# build ble as a static library +add_library(ble_static ${ble_static_sources} ${ble_static_headers}) +target_compile_definitions(ble_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(ble_static ${LIBS}) +linkSharedUtil(ble_static) + +# build ble HL as a dynamic library +add_library(ble_hl MODULE ${ble_hl_sources} ${ble_hl_headers}) +target_link_libraries(ble_hl ble_static ${LIBS}) +linkSharedUtil(ble_hl) + +# build ble HL as a static library +add_library(ble_hl_static ${ble_hl_static_sources} ${ble_hl_static_headers}) +target_compile_definitions(ble_hl_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(ble_hl_static ble_static ${LIBS}) +linkSharedUtil(ble_hl_static) + +add_module_to_solution(ble) + +add_subdirectory(tests) + +if(install_executables) + install(TARGETS ble LIBRARY DESTINATION lib) +endif() diff --git a/modules/ble/README.md b/modules/ble/README.md new file mode 100644 index 00000000..42ff91e2 --- /dev/null +++ b/modules/ble/README.md @@ -0,0 +1 @@ +# BLE (Bluetooth Low Energy) Module \ No newline at end of file diff --git a/modules/ble/deps/linux/dbus-bluez/inc/bluez_characteristic.h b/modules/ble/deps/linux/dbus-bluez/inc/bluez_characteristic.h new file mode 100644 index 00000000..c3504419 --- /dev/null +++ b/modules/ble/deps/linux/dbus-bluez/inc/bluez_characteristic.h @@ -0,0 +1,674 @@ +/* + * Generated by gdbus-codegen 2.40.0. DO NOT EDIT. + * + * The license of this code is the same as for the source it was derived from. + */ + +#ifndef __BLUEZ_CHARACTERISTIC_H__ +#define __BLUEZ_CHARACTERISTIC_H__ + +#include + +G_BEGIN_DECLS + + +/* ------------------------------------------------------------------------ */ +/* Declarations for org.freedesktop.DBus.Introspectable */ + +#define BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (bluez_characteristic_org_freedesktop_dbus_introspectable_get_type ()) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluezcharacteristicOrgFreedesktopDBusIntrospectable)) +#define BLUEZ_CHARACTERISTIC_IS_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE)) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluezcharacteristicOrgFreedesktopDBusIntrospectableIface)) + +struct _bluezcharacteristicOrgFreedesktopDBusIntrospectable; +typedef struct _bluezcharacteristicOrgFreedesktopDBusIntrospectable bluezcharacteristicOrgFreedesktopDBusIntrospectable; +typedef struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableIface bluezcharacteristicOrgFreedesktopDBusIntrospectableIface; + +struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableIface +{ + GTypeInterface parent_iface; + + gboolean (*handle_introspect) ( + bluezcharacteristicOrgFreedesktopDBusIntrospectable *object, + GDBusMethodInvocation *invocation); + +}; + +GType bluez_characteristic_org_freedesktop_dbus_introspectable_get_type (void) G_GNUC_CONST; + +GDBusInterfaceInfo *bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info (void); +guint bluez_characteristic_org_freedesktop_dbus_introspectable_override_properties (GObjectClass *klass, guint property_id_begin); + + +/* D-Bus method call completion functions: */ +void bluez_characteristic_org_freedesktop_dbus_introspectable_complete_introspect ( + bluezcharacteristicOrgFreedesktopDBusIntrospectable *object, + GDBusMethodInvocation *invocation, + const gchar *xml); + + + +/* D-Bus method calls: */ +void bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect ( + bluezcharacteristicOrgFreedesktopDBusIntrospectable *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect_finish ( + bluezcharacteristicOrgFreedesktopDBusIntrospectable *proxy, + gchar **out_xml, + GAsyncResult *res, + GError **error); + +gboolean bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect_sync ( + bluezcharacteristicOrgFreedesktopDBusIntrospectable *proxy, + gchar **out_xml, + GCancellable *cancellable, + GError **error); + + + +/* ---- */ + +#define BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY (bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_get_type ()) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy)) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyClass)) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyClass)) +#define BLUEZ_CHARACTERISTIC_IS_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY)) +#define BLUEZ_CHARACTERISTIC_IS_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY)) + +typedef struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy; +typedef struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyClass bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyClass; +typedef struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyPrivate bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyPrivate; + +struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy +{ + /*< private >*/ + GDBusProxy parent_instance; + bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyPrivate *priv; +}; + +struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyClass +{ + GDBusProxyClass parent_class; +}; + +GType bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_get_type (void) G_GNUC_CONST; + +void bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezcharacteristicOrgFreedesktopDBusIntrospectable *bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_finish ( + GAsyncResult *res, + GError **error); +bluezcharacteristicOrgFreedesktopDBusIntrospectable *bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + +void bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezcharacteristicOrgFreedesktopDBusIntrospectable *bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error); +bluezcharacteristicOrgFreedesktopDBusIntrospectable *bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + + +/* ---- */ + +#define BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_get_type ()) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON, bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton)) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON, bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonClass)) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON, bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonClass)) +#define BLUEZ_CHARACTERISTIC_IS_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON)) +#define BLUEZ_CHARACTERISTIC_IS_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON)) + +typedef struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton; +typedef struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonClass bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonClass; +typedef struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonPrivate bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonPrivate; + +struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton +{ + /*< private >*/ + GDBusInterfaceSkeleton parent_instance; + bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonPrivate *priv; +}; + +struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonClass +{ + GDBusInterfaceSkeletonClass parent_class; +}; + +GType bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_get_type (void) G_GNUC_CONST; + +bluezcharacteristicOrgFreedesktopDBusIntrospectable *bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_new (void); + + +/* ------------------------------------------------------------------------ */ +/* Declarations for org.bluez.GattCharacteristic1 */ + +#define BLUEZ_CHARACTERISTIC_TYPE_ (bluez_characteristic__get_type ()) +#define BLUEZ_CHARACTERISTIC_(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_CHARACTERISTIC_TYPE_, bluezcharacteristic)) +#define BLUEZ_CHARACTERISTIC_IS_(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_CHARACTERISTIC_TYPE_)) +#define BLUEZ_CHARACTERISTIC__GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), BLUEZ_CHARACTERISTIC_TYPE_, bluezcharacteristicIface)) + +struct _bluezcharacteristic; +typedef struct _bluezcharacteristic bluezcharacteristic; +typedef struct _bluezcharacteristicIface bluezcharacteristicIface; + +struct _bluezcharacteristicIface +{ + GTypeInterface parent_iface; + + + gboolean (*handle_read_value) ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation); + + gboolean (*handle_start_notify) ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation); + + gboolean (*handle_stop_notify) ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation); + + gboolean (*handle_write_value) ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation, + const gchar *arg_value); + + const gchar *const * (*get_descriptors) (bluezcharacteristic *object); + + const gchar *const * (*get_flags) (bluezcharacteristic *object); + + gboolean (*get_notifying) (bluezcharacteristic *object); + + const gchar * (*get_service) (bluezcharacteristic *object); + + const gchar * (*get_uuid) (bluezcharacteristic *object); + + const gchar * (*get_value) (bluezcharacteristic *object); + +}; + +GType bluez_characteristic__get_type (void) G_GNUC_CONST; + +GDBusInterfaceInfo *bluez_characteristic__interface_info (void); +guint bluez_characteristic__override_properties (GObjectClass *klass, guint property_id_begin); + + +/* D-Bus method call completion functions: */ +void bluez_characteristic__complete_read_value ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation, + const gchar *value); + +void bluez_characteristic__complete_write_value ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation); + +void bluez_characteristic__complete_start_notify ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation); + +void bluez_characteristic__complete_stop_notify ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation); + + + +/* D-Bus method calls: */ +void bluez_characteristic__call_read_value ( + bluezcharacteristic *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_characteristic__call_read_value_finish ( + bluezcharacteristic *proxy, + gchar **out_value, + GAsyncResult *res, + GError **error); + +gboolean bluez_characteristic__call_read_value_sync ( + bluezcharacteristic *proxy, + gchar **out_value, + GCancellable *cancellable, + GError **error); + +void bluez_characteristic__call_write_value ( + bluezcharacteristic *proxy, + const gchar *arg_value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_characteristic__call_write_value_finish ( + bluezcharacteristic *proxy, + GAsyncResult *res, + GError **error); + +gboolean bluez_characteristic__call_write_value_sync ( + bluezcharacteristic *proxy, + const gchar *arg_value, + GCancellable *cancellable, + GError **error); + +void bluez_characteristic__call_start_notify ( + bluezcharacteristic *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_characteristic__call_start_notify_finish ( + bluezcharacteristic *proxy, + GAsyncResult *res, + GError **error); + +gboolean bluez_characteristic__call_start_notify_sync ( + bluezcharacteristic *proxy, + GCancellable *cancellable, + GError **error); + +void bluez_characteristic__call_stop_notify ( + bluezcharacteristic *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_characteristic__call_stop_notify_finish ( + bluezcharacteristic *proxy, + GAsyncResult *res, + GError **error); + +gboolean bluez_characteristic__call_stop_notify_sync ( + bluezcharacteristic *proxy, + GCancellable *cancellable, + GError **error); + + + +/* D-Bus property accessors: */ +const gchar *bluez_characteristic__get_uuid (bluezcharacteristic *object); +gchar *bluez_characteristic__dup_uuid (bluezcharacteristic *object); +void bluez_characteristic__set_uuid (bluezcharacteristic *object, const gchar *value); + +const gchar *bluez_characteristic__get_service (bluezcharacteristic *object); +gchar *bluez_characteristic__dup_service (bluezcharacteristic *object); +void bluez_characteristic__set_service (bluezcharacteristic *object, const gchar *value); + +const gchar *bluez_characteristic__get_value (bluezcharacteristic *object); +gchar *bluez_characteristic__dup_value (bluezcharacteristic *object); +void bluez_characteristic__set_value (bluezcharacteristic *object, const gchar *value); + +gboolean bluez_characteristic__get_notifying (bluezcharacteristic *object); +void bluez_characteristic__set_notifying (bluezcharacteristic *object, gboolean value); + +const gchar *const *bluez_characteristic__get_flags (bluezcharacteristic *object); +gchar **bluez_characteristic__dup_flags (bluezcharacteristic *object); +void bluez_characteristic__set_flags (bluezcharacteristic *object, const gchar *const *value); + +const gchar *const *bluez_characteristic__get_descriptors (bluezcharacteristic *object); +gchar **bluez_characteristic__dup_descriptors (bluezcharacteristic *object); +void bluez_characteristic__set_descriptors (bluezcharacteristic *object, const gchar *const *value); + + +/* ---- */ + +#define BLUEZ_CHARACTERISTIC_TYPE__PROXY (bluez_characteristic__proxy_get_type ()) +#define BLUEZ_CHARACTERISTIC__PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_CHARACTERISTIC_TYPE__PROXY, bluezcharacteristicProxy)) +#define BLUEZ_CHARACTERISTIC__PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_CHARACTERISTIC_TYPE__PROXY, bluezcharacteristicProxyClass)) +#define BLUEZ_CHARACTERISTIC__PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_CHARACTERISTIC_TYPE__PROXY, bluezcharacteristicProxyClass)) +#define BLUEZ_CHARACTERISTIC_IS__PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_CHARACTERISTIC_TYPE__PROXY)) +#define BLUEZ_CHARACTERISTIC_IS__PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_CHARACTERISTIC_TYPE__PROXY)) + +typedef struct _bluezcharacteristicProxy bluezcharacteristicProxy; +typedef struct _bluezcharacteristicProxyClass bluezcharacteristicProxyClass; +typedef struct _bluezcharacteristicProxyPrivate bluezcharacteristicProxyPrivate; + +struct _bluezcharacteristicProxy +{ + /*< private >*/ + GDBusProxy parent_instance; + bluezcharacteristicProxyPrivate *priv; +}; + +struct _bluezcharacteristicProxyClass +{ + GDBusProxyClass parent_class; +}; + +GType bluez_characteristic__proxy_get_type (void) G_GNUC_CONST; + +void bluez_characteristic__proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezcharacteristic *bluez_characteristic__proxy_new_finish ( + GAsyncResult *res, + GError **error); +bluezcharacteristic *bluez_characteristic__proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + +void bluez_characteristic__proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezcharacteristic *bluez_characteristic__proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error); +bluezcharacteristic *bluez_characteristic__proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + + +/* ---- */ + +#define BLUEZ_CHARACTERISTIC_TYPE__SKELETON (bluez_characteristic__skeleton_get_type ()) +#define BLUEZ_CHARACTERISTIC__SKELETON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_CHARACTERISTIC_TYPE__SKELETON, bluezcharacteristicSkeleton)) +#define BLUEZ_CHARACTERISTIC__SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_CHARACTERISTIC_TYPE__SKELETON, bluezcharacteristicSkeletonClass)) +#define BLUEZ_CHARACTERISTIC__SKELETON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_CHARACTERISTIC_TYPE__SKELETON, bluezcharacteristicSkeletonClass)) +#define BLUEZ_CHARACTERISTIC_IS__SKELETON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_CHARACTERISTIC_TYPE__SKELETON)) +#define BLUEZ_CHARACTERISTIC_IS__SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_CHARACTERISTIC_TYPE__SKELETON)) + +typedef struct _bluezcharacteristicSkeleton bluezcharacteristicSkeleton; +typedef struct _bluezcharacteristicSkeletonClass bluezcharacteristicSkeletonClass; +typedef struct _bluezcharacteristicSkeletonPrivate bluezcharacteristicSkeletonPrivate; + +struct _bluezcharacteristicSkeleton +{ + /*< private >*/ + GDBusInterfaceSkeleton parent_instance; + bluezcharacteristicSkeletonPrivate *priv; +}; + +struct _bluezcharacteristicSkeletonClass +{ + GDBusInterfaceSkeletonClass parent_class; +}; + +GType bluez_characteristic__skeleton_get_type (void) G_GNUC_CONST; + +bluezcharacteristic *bluez_characteristic__skeleton_new (void); + + +/* ------------------------------------------------------------------------ */ +/* Declarations for org.freedesktop.DBus.Properties */ + +#define BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES (bluez_characteristic_org_freedesktop_dbus_properties_get_type ()) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluezcharacteristicOrgFreedesktopDBusProperties)) +#define BLUEZ_CHARACTERISTIC_IS_ORG_FREEDESKTOP_DBUS_PROPERTIES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES)) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluezcharacteristicOrgFreedesktopDBusPropertiesIface)) + +struct _bluezcharacteristicOrgFreedesktopDBusProperties; +typedef struct _bluezcharacteristicOrgFreedesktopDBusProperties bluezcharacteristicOrgFreedesktopDBusProperties; +typedef struct _bluezcharacteristicOrgFreedesktopDBusPropertiesIface bluezcharacteristicOrgFreedesktopDBusPropertiesIface; + +struct _bluezcharacteristicOrgFreedesktopDBusPropertiesIface +{ + GTypeInterface parent_iface; + + + gboolean (*handle_get) ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + const gchar *arg_interface, + const gchar *arg_name); + + gboolean (*handle_get_all) ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + const gchar *arg_interface); + + gboolean (*handle_set) ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + const gchar *arg_interface, + const gchar *arg_name, + GVariant *arg_value); + + void (*properties_changed) ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + const gchar *arg_interface, + GVariant *arg_changed_properties, + const gchar *const *arg_invalidated_properties); + +}; + +GType bluez_characteristic_org_freedesktop_dbus_properties_get_type (void) G_GNUC_CONST; + +GDBusInterfaceInfo *bluez_characteristic_org_freedesktop_dbus_properties_interface_info (void); +guint bluez_characteristic_org_freedesktop_dbus_properties_override_properties (GObjectClass *klass, guint property_id_begin); + + +/* D-Bus method call completion functions: */ +void bluez_characteristic_org_freedesktop_dbus_properties_complete_get ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + GVariant *value); + +void bluez_characteristic_org_freedesktop_dbus_properties_complete_set ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation); + +void bluez_characteristic_org_freedesktop_dbus_properties_complete_get_all ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + GVariant *properties); + + + +/* D-Bus signal emissions functions: */ +void bluez_characteristic_org_freedesktop_dbus_properties_emit_properties_changed ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + const gchar *arg_interface, + GVariant *arg_changed_properties, + const gchar *const *arg_invalidated_properties); + + + +/* D-Bus method calls: */ +void bluez_characteristic_org_freedesktop_dbus_properties_call_get ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_characteristic_org_freedesktop_dbus_properties_call_get_finish ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + GVariant **out_value, + GAsyncResult *res, + GError **error); + +gboolean bluez_characteristic_org_freedesktop_dbus_properties_call_get_sync ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant **out_value, + GCancellable *cancellable, + GError **error); + +void bluez_characteristic_org_freedesktop_dbus_properties_call_set ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant *arg_value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_characteristic_org_freedesktop_dbus_properties_call_set_finish ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + GAsyncResult *res, + GError **error); + +gboolean bluez_characteristic_org_freedesktop_dbus_properties_call_set_sync ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant *arg_value, + GCancellable *cancellable, + GError **error); + +void bluez_characteristic_org_freedesktop_dbus_properties_call_get_all ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_characteristic_org_freedesktop_dbus_properties_call_get_all_finish ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + GVariant **out_properties, + GAsyncResult *res, + GError **error); + +gboolean bluez_characteristic_org_freedesktop_dbus_properties_call_get_all_sync ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + GVariant **out_properties, + GCancellable *cancellable, + GError **error); + + + +/* ---- */ + +#define BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY (bluez_characteristic_org_freedesktop_dbus_properties_proxy_get_type ()) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, bluezcharacteristicOrgFreedesktopDBusPropertiesProxy)) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, bluezcharacteristicOrgFreedesktopDBusPropertiesProxyClass)) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, bluezcharacteristicOrgFreedesktopDBusPropertiesProxyClass)) +#define BLUEZ_CHARACTERISTIC_IS_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY)) +#define BLUEZ_CHARACTERISTIC_IS_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY)) + +typedef struct _bluezcharacteristicOrgFreedesktopDBusPropertiesProxy bluezcharacteristicOrgFreedesktopDBusPropertiesProxy; +typedef struct _bluezcharacteristicOrgFreedesktopDBusPropertiesProxyClass bluezcharacteristicOrgFreedesktopDBusPropertiesProxyClass; +typedef struct _bluezcharacteristicOrgFreedesktopDBusPropertiesProxyPrivate bluezcharacteristicOrgFreedesktopDBusPropertiesProxyPrivate; + +struct _bluezcharacteristicOrgFreedesktopDBusPropertiesProxy +{ + /*< private >*/ + GDBusProxy parent_instance; + bluezcharacteristicOrgFreedesktopDBusPropertiesProxyPrivate *priv; +}; + +struct _bluezcharacteristicOrgFreedesktopDBusPropertiesProxyClass +{ + GDBusProxyClass parent_class; +}; + +GType bluez_characteristic_org_freedesktop_dbus_properties_proxy_get_type (void) G_GNUC_CONST; + +void bluez_characteristic_org_freedesktop_dbus_properties_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezcharacteristicOrgFreedesktopDBusProperties *bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_finish ( + GAsyncResult *res, + GError **error); +bluezcharacteristicOrgFreedesktopDBusProperties *bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + +void bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezcharacteristicOrgFreedesktopDBusProperties *bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error); +bluezcharacteristicOrgFreedesktopDBusProperties *bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + + +/* ---- */ + +#define BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (bluez_characteristic_org_freedesktop_dbus_properties_skeleton_get_type ()) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON, bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton)) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON, bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonClass)) +#define BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON, bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonClass)) +#define BLUEZ_CHARACTERISTIC_IS_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON)) +#define BLUEZ_CHARACTERISTIC_IS_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON)) + +typedef struct _bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton; +typedef struct _bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonClass bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonClass; +typedef struct _bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonPrivate bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonPrivate; + +struct _bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton +{ + /*< private >*/ + GDBusInterfaceSkeleton parent_instance; + bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonPrivate *priv; +}; + +struct _bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonClass +{ + GDBusInterfaceSkeletonClass parent_class; +}; + +GType bluez_characteristic_org_freedesktop_dbus_properties_skeleton_get_type (void) G_GNUC_CONST; + +bluezcharacteristicOrgFreedesktopDBusProperties *bluez_characteristic_org_freedesktop_dbus_properties_skeleton_new (void); + + +G_END_DECLS + +#endif /* __BLUEZ_CHARACTERISTIC_H__ */ diff --git a/modules/ble/deps/linux/dbus-bluez/inc/bluez_device.h b/modules/ble/deps/linux/dbus-bluez/inc/bluez_device.h new file mode 100644 index 00000000..492f97ab --- /dev/null +++ b/modules/ble/deps/linux/dbus-bluez/inc/bluez_device.h @@ -0,0 +1,926 @@ +/* + * Generated by gdbus-codegen 2.40.0. DO NOT EDIT. + * + * The license of this code is the same as for the source it was derived from. + */ + +#ifndef __BLUEZ_DEVICE_H__ +#define __BLUEZ_DEVICE_H__ + +#include + +G_BEGIN_DECLS + + +/* ------------------------------------------------------------------------ */ +/* Declarations for org.freedesktop.DBus.Introspectable */ + +#define BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (bluez_device_org_freedesktop_dbus_introspectable_get_type ()) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluezdeviceOrgFreedesktopDBusIntrospectable)) +#define BLUEZ_DEVICE_IS_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE)) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluezdeviceOrgFreedesktopDBusIntrospectableIface)) + +struct _bluezdeviceOrgFreedesktopDBusIntrospectable; +typedef struct _bluezdeviceOrgFreedesktopDBusIntrospectable bluezdeviceOrgFreedesktopDBusIntrospectable; +typedef struct _bluezdeviceOrgFreedesktopDBusIntrospectableIface bluezdeviceOrgFreedesktopDBusIntrospectableIface; + +struct _bluezdeviceOrgFreedesktopDBusIntrospectableIface +{ + GTypeInterface parent_iface; + + gboolean (*handle_introspect) ( + bluezdeviceOrgFreedesktopDBusIntrospectable *object, + GDBusMethodInvocation *invocation); + +}; + +GType bluez_device_org_freedesktop_dbus_introspectable_get_type (void) G_GNUC_CONST; + +GDBusInterfaceInfo *bluez_device_org_freedesktop_dbus_introspectable_interface_info (void); +guint bluez_device_org_freedesktop_dbus_introspectable_override_properties (GObjectClass *klass, guint property_id_begin); + + +/* D-Bus method call completion functions: */ +void bluez_device_org_freedesktop_dbus_introspectable_complete_introspect ( + bluezdeviceOrgFreedesktopDBusIntrospectable *object, + GDBusMethodInvocation *invocation, + const gchar *xml); + + + +/* D-Bus method calls: */ +void bluez_device_org_freedesktop_dbus_introspectable_call_introspect ( + bluezdeviceOrgFreedesktopDBusIntrospectable *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_device_org_freedesktop_dbus_introspectable_call_introspect_finish ( + bluezdeviceOrgFreedesktopDBusIntrospectable *proxy, + gchar **out_xml, + GAsyncResult *res, + GError **error); + +gboolean bluez_device_org_freedesktop_dbus_introspectable_call_introspect_sync ( + bluezdeviceOrgFreedesktopDBusIntrospectable *proxy, + gchar **out_xml, + GCancellable *cancellable, + GError **error); + + + +/* ---- */ + +#define BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY (bluez_device_org_freedesktop_dbus_introspectable_proxy_get_type ()) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, bluezdeviceOrgFreedesktopDBusIntrospectableProxy)) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, bluezdeviceOrgFreedesktopDBusIntrospectableProxyClass)) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, bluezdeviceOrgFreedesktopDBusIntrospectableProxyClass)) +#define BLUEZ_DEVICE_IS_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY)) +#define BLUEZ_DEVICE_IS_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY)) + +typedef struct _bluezdeviceOrgFreedesktopDBusIntrospectableProxy bluezdeviceOrgFreedesktopDBusIntrospectableProxy; +typedef struct _bluezdeviceOrgFreedesktopDBusIntrospectableProxyClass bluezdeviceOrgFreedesktopDBusIntrospectableProxyClass; +typedef struct _bluezdeviceOrgFreedesktopDBusIntrospectableProxyPrivate bluezdeviceOrgFreedesktopDBusIntrospectableProxyPrivate; + +struct _bluezdeviceOrgFreedesktopDBusIntrospectableProxy +{ + /*< private >*/ + GDBusProxy parent_instance; + bluezdeviceOrgFreedesktopDBusIntrospectableProxyPrivate *priv; +}; + +struct _bluezdeviceOrgFreedesktopDBusIntrospectableProxyClass +{ + GDBusProxyClass parent_class; +}; + +GType bluez_device_org_freedesktop_dbus_introspectable_proxy_get_type (void) G_GNUC_CONST; + +void bluez_device_org_freedesktop_dbus_introspectable_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezdeviceOrgFreedesktopDBusIntrospectable *bluez_device_org_freedesktop_dbus_introspectable_proxy_new_finish ( + GAsyncResult *res, + GError **error); +bluezdeviceOrgFreedesktopDBusIntrospectable *bluez_device_org_freedesktop_dbus_introspectable_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + +void bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezdeviceOrgFreedesktopDBusIntrospectable *bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error); +bluezdeviceOrgFreedesktopDBusIntrospectable *bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + + +/* ---- */ + +#define BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (bluez_device_org_freedesktop_dbus_introspectable_skeleton_get_type ()) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON, bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton)) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON, bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonClass)) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON, bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonClass)) +#define BLUEZ_DEVICE_IS_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON)) +#define BLUEZ_DEVICE_IS_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON)) + +typedef struct _bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton; +typedef struct _bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonClass bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonClass; +typedef struct _bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonPrivate bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonPrivate; + +struct _bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton +{ + /*< private >*/ + GDBusInterfaceSkeleton parent_instance; + bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonPrivate *priv; +}; + +struct _bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonClass +{ + GDBusInterfaceSkeletonClass parent_class; +}; + +GType bluez_device_org_freedesktop_dbus_introspectable_skeleton_get_type (void) G_GNUC_CONST; + +bluezdeviceOrgFreedesktopDBusIntrospectable *bluez_device_org_freedesktop_dbus_introspectable_skeleton_new (void); + + +/* ------------------------------------------------------------------------ */ +/* Declarations for org.bluez.Device1 */ + +#define BLUEZ_DEVICE_TYPE_ (bluez_device__get_type ()) +#define BLUEZ_DEVICE_(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE_, bluezdevice)) +#define BLUEZ_DEVICE_IS_(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE_)) +#define BLUEZ_DEVICE__GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), BLUEZ_DEVICE_TYPE_, bluezdeviceIface)) + +struct _bluezdevice; +typedef struct _bluezdevice bluezdevice; +typedef struct _bluezdeviceIface bluezdeviceIface; + +struct _bluezdeviceIface +{ + GTypeInterface parent_iface; + + + gboolean (*handle_cancel_pairing) ( + bluezdevice *object, + GDBusMethodInvocation *invocation); + + gboolean (*handle_connect) ( + bluezdevice *object, + GDBusMethodInvocation *invocation); + + gboolean (*handle_connect_profile) ( + bluezdevice *object, + GDBusMethodInvocation *invocation, + const gchar *arg_UUID); + + gboolean (*handle_disconnect) ( + bluezdevice *object, + GDBusMethodInvocation *invocation); + + gboolean (*handle_disconnect_profile) ( + bluezdevice *object, + GDBusMethodInvocation *invocation, + const gchar *arg_UUID); + + gboolean (*handle_pair) ( + bluezdevice *object, + GDBusMethodInvocation *invocation); + + const gchar * (*get_adapter) (bluezdevice *object); + + const gchar * (*get_address) (bluezdevice *object); + + const gchar * (*get_alias) (bluezdevice *object); + + guint16 (*get_appearance) (bluezdevice *object); + + gboolean (*get_blocked) (bluezdevice *object); + + guint (*get_class) (bluezdevice *object); + + gboolean (*get_connected) (bluezdevice *object); + + const gchar *const * (*get_gatt_services) (bluezdevice *object); + + const gchar * (*get_icon) (bluezdevice *object); + + gboolean (*get_legacy_pairing) (bluezdevice *object); + + GVariant * (*get_manufacturer_data) (bluezdevice *object); + + const gchar * (*get_modalias) (bluezdevice *object); + + const gchar * (*get_name) (bluezdevice *object); + + gboolean (*get_paired) (bluezdevice *object); + + gint16 (*get_rssi) (bluezdevice *object); + + GVariant * (*get_service_data) (bluezdevice *object); + + gboolean (*get_trusted) (bluezdevice *object); + + gint16 (*get_tx_power) (bluezdevice *object); + + const gchar *const * (*get_uuids) (bluezdevice *object); + +}; + +GType bluez_device__get_type (void) G_GNUC_CONST; + +GDBusInterfaceInfo *bluez_device__interface_info (void); +guint bluez_device__override_properties (GObjectClass *klass, guint property_id_begin); + + +/* D-Bus method call completion functions: */ +void bluez_device__complete_disconnect ( + bluezdevice *object, + GDBusMethodInvocation *invocation); + +void bluez_device__complete_connect ( + bluezdevice *object, + GDBusMethodInvocation *invocation); + +void bluez_device__complete_connect_profile ( + bluezdevice *object, + GDBusMethodInvocation *invocation); + +void bluez_device__complete_disconnect_profile ( + bluezdevice *object, + GDBusMethodInvocation *invocation); + +void bluez_device__complete_pair ( + bluezdevice *object, + GDBusMethodInvocation *invocation); + +void bluez_device__complete_cancel_pairing ( + bluezdevice *object, + GDBusMethodInvocation *invocation); + + + +/* D-Bus method calls: */ +void bluez_device__call_disconnect ( + bluezdevice *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_device__call_disconnect_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error); + +gboolean bluez_device__call_disconnect_sync ( + bluezdevice *proxy, + GCancellable *cancellable, + GError **error); + +void bluez_device__call_connect ( + bluezdevice *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_device__call_connect_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error); + +gboolean bluez_device__call_connect_sync ( + bluezdevice *proxy, + GCancellable *cancellable, + GError **error); + +void bluez_device__call_connect_profile ( + bluezdevice *proxy, + const gchar *arg_UUID, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_device__call_connect_profile_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error); + +gboolean bluez_device__call_connect_profile_sync ( + bluezdevice *proxy, + const gchar *arg_UUID, + GCancellable *cancellable, + GError **error); + +void bluez_device__call_disconnect_profile ( + bluezdevice *proxy, + const gchar *arg_UUID, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_device__call_disconnect_profile_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error); + +gboolean bluez_device__call_disconnect_profile_sync ( + bluezdevice *proxy, + const gchar *arg_UUID, + GCancellable *cancellable, + GError **error); + +void bluez_device__call_pair ( + bluezdevice *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_device__call_pair_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error); + +gboolean bluez_device__call_pair_sync ( + bluezdevice *proxy, + GCancellable *cancellable, + GError **error); + +void bluez_device__call_cancel_pairing ( + bluezdevice *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_device__call_cancel_pairing_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error); + +gboolean bluez_device__call_cancel_pairing_sync ( + bluezdevice *proxy, + GCancellable *cancellable, + GError **error); + + + +/* D-Bus property accessors: */ +const gchar *bluez_device__get_address (bluezdevice *object); +gchar *bluez_device__dup_address (bluezdevice *object); +void bluez_device__set_address (bluezdevice *object, const gchar *value); + +const gchar *bluez_device__get_name (bluezdevice *object); +gchar *bluez_device__dup_name (bluezdevice *object); +void bluez_device__set_name (bluezdevice *object, const gchar *value); + +const gchar *bluez_device__get_alias (bluezdevice *object); +gchar *bluez_device__dup_alias (bluezdevice *object); +void bluez_device__set_alias (bluezdevice *object, const gchar *value); + +guint bluez_device__get_class (bluezdevice *object); +void bluez_device__set_class (bluezdevice *object, guint value); + +guint16 bluez_device__get_appearance (bluezdevice *object); +void bluez_device__set_appearance (bluezdevice *object, guint16 value); + +const gchar *bluez_device__get_icon (bluezdevice *object); +gchar *bluez_device__dup_icon (bluezdevice *object); +void bluez_device__set_icon (bluezdevice *object, const gchar *value); + +gboolean bluez_device__get_paired (bluezdevice *object); +void bluez_device__set_paired (bluezdevice *object, gboolean value); + +gboolean bluez_device__get_trusted (bluezdevice *object); +void bluez_device__set_trusted (bluezdevice *object, gboolean value); + +gboolean bluez_device__get_blocked (bluezdevice *object); +void bluez_device__set_blocked (bluezdevice *object, gboolean value); + +gboolean bluez_device__get_legacy_pairing (bluezdevice *object); +void bluez_device__set_legacy_pairing (bluezdevice *object, gboolean value); + +gint16 bluez_device__get_rssi (bluezdevice *object); +void bluez_device__set_rssi (bluezdevice *object, gint16 value); + +gboolean bluez_device__get_connected (bluezdevice *object); +void bluez_device__set_connected (bluezdevice *object, gboolean value); + +const gchar *const *bluez_device__get_uuids (bluezdevice *object); +gchar **bluez_device__dup_uuids (bluezdevice *object); +void bluez_device__set_uuids (bluezdevice *object, const gchar *const *value); + +const gchar *bluez_device__get_modalias (bluezdevice *object); +gchar *bluez_device__dup_modalias (bluezdevice *object); +void bluez_device__set_modalias (bluezdevice *object, const gchar *value); + +const gchar *bluez_device__get_adapter (bluezdevice *object); +gchar *bluez_device__dup_adapter (bluezdevice *object); +void bluez_device__set_adapter (bluezdevice *object, const gchar *value); + +GVariant *bluez_device__get_manufacturer_data (bluezdevice *object); +GVariant *bluez_device__dup_manufacturer_data (bluezdevice *object); +void bluez_device__set_manufacturer_data (bluezdevice *object, GVariant *value); + +GVariant *bluez_device__get_service_data (bluezdevice *object); +GVariant *bluez_device__dup_service_data (bluezdevice *object); +void bluez_device__set_service_data (bluezdevice *object, GVariant *value); + +gint16 bluez_device__get_tx_power (bluezdevice *object); +void bluez_device__set_tx_power (bluezdevice *object, gint16 value); + +const gchar *const *bluez_device__get_gatt_services (bluezdevice *object); +gchar **bluez_device__dup_gatt_services (bluezdevice *object); +void bluez_device__set_gatt_services (bluezdevice *object, const gchar *const *value); + + +/* ---- */ + +#define BLUEZ_DEVICE_TYPE__PROXY (bluez_device__proxy_get_type ()) +#define BLUEZ_DEVICE__PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE__PROXY, bluezdeviceProxy)) +#define BLUEZ_DEVICE__PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_DEVICE_TYPE__PROXY, bluezdeviceProxyClass)) +#define BLUEZ_DEVICE__PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_DEVICE_TYPE__PROXY, bluezdeviceProxyClass)) +#define BLUEZ_DEVICE_IS__PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE__PROXY)) +#define BLUEZ_DEVICE_IS__PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_DEVICE_TYPE__PROXY)) + +typedef struct _bluezdeviceProxy bluezdeviceProxy; +typedef struct _bluezdeviceProxyClass bluezdeviceProxyClass; +typedef struct _bluezdeviceProxyPrivate bluezdeviceProxyPrivate; + +struct _bluezdeviceProxy +{ + /*< private >*/ + GDBusProxy parent_instance; + bluezdeviceProxyPrivate *priv; +}; + +struct _bluezdeviceProxyClass +{ + GDBusProxyClass parent_class; +}; + +GType bluez_device__proxy_get_type (void) G_GNUC_CONST; + +void bluez_device__proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezdevice *bluez_device__proxy_new_finish ( + GAsyncResult *res, + GError **error); +bluezdevice *bluez_device__proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + +void bluez_device__proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezdevice *bluez_device__proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error); +bluezdevice *bluez_device__proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + + +/* ---- */ + +#define BLUEZ_DEVICE_TYPE__SKELETON (bluez_device__skeleton_get_type ()) +#define BLUEZ_DEVICE__SKELETON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE__SKELETON, bluezdeviceSkeleton)) +#define BLUEZ_DEVICE__SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_DEVICE_TYPE__SKELETON, bluezdeviceSkeletonClass)) +#define BLUEZ_DEVICE__SKELETON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_DEVICE_TYPE__SKELETON, bluezdeviceSkeletonClass)) +#define BLUEZ_DEVICE_IS__SKELETON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE__SKELETON)) +#define BLUEZ_DEVICE_IS__SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_DEVICE_TYPE__SKELETON)) + +typedef struct _bluezdeviceSkeleton bluezdeviceSkeleton; +typedef struct _bluezdeviceSkeletonClass bluezdeviceSkeletonClass; +typedef struct _bluezdeviceSkeletonPrivate bluezdeviceSkeletonPrivate; + +struct _bluezdeviceSkeleton +{ + /*< private >*/ + GDBusInterfaceSkeleton parent_instance; + bluezdeviceSkeletonPrivate *priv; +}; + +struct _bluezdeviceSkeletonClass +{ + GDBusInterfaceSkeletonClass parent_class; +}; + +GType bluez_device__skeleton_get_type (void) G_GNUC_CONST; + +bluezdevice *bluez_device__skeleton_new (void); + + +/* ------------------------------------------------------------------------ */ +/* Declarations for org.freedesktop.DBus.Properties */ + +#define BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES (bluez_device_org_freedesktop_dbus_properties_get_type ()) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluezdeviceOrgFreedesktopDBusProperties)) +#define BLUEZ_DEVICE_IS_ORG_FREEDESKTOP_DBUS_PROPERTIES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES)) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluezdeviceOrgFreedesktopDBusPropertiesIface)) + +struct _bluezdeviceOrgFreedesktopDBusProperties; +typedef struct _bluezdeviceOrgFreedesktopDBusProperties bluezdeviceOrgFreedesktopDBusProperties; +typedef struct _bluezdeviceOrgFreedesktopDBusPropertiesIface bluezdeviceOrgFreedesktopDBusPropertiesIface; + +struct _bluezdeviceOrgFreedesktopDBusPropertiesIface +{ + GTypeInterface parent_iface; + + + gboolean (*handle_get) ( + bluezdeviceOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + const gchar *arg_interface, + const gchar *arg_name); + + gboolean (*handle_get_all) ( + bluezdeviceOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + const gchar *arg_interface); + + gboolean (*handle_set) ( + bluezdeviceOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + const gchar *arg_interface, + const gchar *arg_name, + GVariant *arg_value); + + void (*properties_changed) ( + bluezdeviceOrgFreedesktopDBusProperties *object, + const gchar *arg_interface, + GVariant *arg_changed_properties, + const gchar *const *arg_invalidated_properties); + +}; + +GType bluez_device_org_freedesktop_dbus_properties_get_type (void) G_GNUC_CONST; + +GDBusInterfaceInfo *bluez_device_org_freedesktop_dbus_properties_interface_info (void); +guint bluez_device_org_freedesktop_dbus_properties_override_properties (GObjectClass *klass, guint property_id_begin); + + +/* D-Bus method call completion functions: */ +void bluez_device_org_freedesktop_dbus_properties_complete_get ( + bluezdeviceOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + GVariant *value); + +void bluez_device_org_freedesktop_dbus_properties_complete_set ( + bluezdeviceOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation); + +void bluez_device_org_freedesktop_dbus_properties_complete_get_all ( + bluezdeviceOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + GVariant *properties); + + + +/* D-Bus signal emissions functions: */ +void bluez_device_org_freedesktop_dbus_properties_emit_properties_changed ( + bluezdeviceOrgFreedesktopDBusProperties *object, + const gchar *arg_interface, + GVariant *arg_changed_properties, + const gchar *const *arg_invalidated_properties); + + + +/* D-Bus method calls: */ +void bluez_device_org_freedesktop_dbus_properties_call_get ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_device_org_freedesktop_dbus_properties_call_get_finish ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + GVariant **out_value, + GAsyncResult *res, + GError **error); + +gboolean bluez_device_org_freedesktop_dbus_properties_call_get_sync ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant **out_value, + GCancellable *cancellable, + GError **error); + +void bluez_device_org_freedesktop_dbus_properties_call_set ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant *arg_value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_device_org_freedesktop_dbus_properties_call_set_finish ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + GAsyncResult *res, + GError **error); + +gboolean bluez_device_org_freedesktop_dbus_properties_call_set_sync ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant *arg_value, + GCancellable *cancellable, + GError **error); + +void bluez_device_org_freedesktop_dbus_properties_call_get_all ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean bluez_device_org_freedesktop_dbus_properties_call_get_all_finish ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + GVariant **out_properties, + GAsyncResult *res, + GError **error); + +gboolean bluez_device_org_freedesktop_dbus_properties_call_get_all_sync ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + GVariant **out_properties, + GCancellable *cancellable, + GError **error); + + + +/* ---- */ + +#define BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY (bluez_device_org_freedesktop_dbus_properties_proxy_get_type ()) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, bluezdeviceOrgFreedesktopDBusPropertiesProxy)) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, bluezdeviceOrgFreedesktopDBusPropertiesProxyClass)) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, bluezdeviceOrgFreedesktopDBusPropertiesProxyClass)) +#define BLUEZ_DEVICE_IS_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY)) +#define BLUEZ_DEVICE_IS_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY)) + +typedef struct _bluezdeviceOrgFreedesktopDBusPropertiesProxy bluezdeviceOrgFreedesktopDBusPropertiesProxy; +typedef struct _bluezdeviceOrgFreedesktopDBusPropertiesProxyClass bluezdeviceOrgFreedesktopDBusPropertiesProxyClass; +typedef struct _bluezdeviceOrgFreedesktopDBusPropertiesProxyPrivate bluezdeviceOrgFreedesktopDBusPropertiesProxyPrivate; + +struct _bluezdeviceOrgFreedesktopDBusPropertiesProxy +{ + /*< private >*/ + GDBusProxy parent_instance; + bluezdeviceOrgFreedesktopDBusPropertiesProxyPrivate *priv; +}; + +struct _bluezdeviceOrgFreedesktopDBusPropertiesProxyClass +{ + GDBusProxyClass parent_class; +}; + +GType bluez_device_org_freedesktop_dbus_properties_proxy_get_type (void) G_GNUC_CONST; + +void bluez_device_org_freedesktop_dbus_properties_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezdeviceOrgFreedesktopDBusProperties *bluez_device_org_freedesktop_dbus_properties_proxy_new_finish ( + GAsyncResult *res, + GError **error); +bluezdeviceOrgFreedesktopDBusProperties *bluez_device_org_freedesktop_dbus_properties_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + +void bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezdeviceOrgFreedesktopDBusProperties *bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error); +bluezdeviceOrgFreedesktopDBusProperties *bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + + +/* ---- */ + +#define BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (bluez_device_org_freedesktop_dbus_properties_skeleton_get_type ()) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON, bluezdeviceOrgFreedesktopDBusPropertiesSkeleton)) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON, bluezdeviceOrgFreedesktopDBusPropertiesSkeletonClass)) +#define BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON, bluezdeviceOrgFreedesktopDBusPropertiesSkeletonClass)) +#define BLUEZ_DEVICE_IS_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON)) +#define BLUEZ_DEVICE_IS_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON)) + +typedef struct _bluezdeviceOrgFreedesktopDBusPropertiesSkeleton bluezdeviceOrgFreedesktopDBusPropertiesSkeleton; +typedef struct _bluezdeviceOrgFreedesktopDBusPropertiesSkeletonClass bluezdeviceOrgFreedesktopDBusPropertiesSkeletonClass; +typedef struct _bluezdeviceOrgFreedesktopDBusPropertiesSkeletonPrivate bluezdeviceOrgFreedesktopDBusPropertiesSkeletonPrivate; + +struct _bluezdeviceOrgFreedesktopDBusPropertiesSkeleton +{ + /*< private >*/ + GDBusInterfaceSkeleton parent_instance; + bluezdeviceOrgFreedesktopDBusPropertiesSkeletonPrivate *priv; +}; + +struct _bluezdeviceOrgFreedesktopDBusPropertiesSkeletonClass +{ + GDBusInterfaceSkeletonClass parent_class; +}; + +GType bluez_device_org_freedesktop_dbus_properties_skeleton_get_type (void) G_GNUC_CONST; + +bluezdeviceOrgFreedesktopDBusProperties *bluez_device_org_freedesktop_dbus_properties_skeleton_new (void); + + +/* ------------------------------------------------------------------------ */ +/* Declarations for org.bluez.ProximityReporter1 */ + +#define BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1 (bluez_device_org_bluez_proximity_reporter1_get_type ()) +#define BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1, bluezdeviceOrgBluezProximityReporter1)) +#define BLUEZ_DEVICE_IS_ORG_BLUEZ_PROXIMITY_REPORTER1(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1)) +#define BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1, bluezdeviceOrgBluezProximityReporter1Iface)) + +struct _bluezdeviceOrgBluezProximityReporter1; +typedef struct _bluezdeviceOrgBluezProximityReporter1 bluezdeviceOrgBluezProximityReporter1; +typedef struct _bluezdeviceOrgBluezProximityReporter1Iface bluezdeviceOrgBluezProximityReporter1Iface; + +struct _bluezdeviceOrgBluezProximityReporter1Iface +{ + GTypeInterface parent_iface; + + const gchar * (*get_immediate_alert_level) (bluezdeviceOrgBluezProximityReporter1 *object); + + const gchar * (*get_link_loss_alert_level) (bluezdeviceOrgBluezProximityReporter1 *object); + +}; + +GType bluez_device_org_bluez_proximity_reporter1_get_type (void) G_GNUC_CONST; + +GDBusInterfaceInfo *bluez_device_org_bluez_proximity_reporter1_interface_info (void); +guint bluez_device_org_bluez_proximity_reporter1_override_properties (GObjectClass *klass, guint property_id_begin); + + +/* D-Bus property accessors: */ +const gchar *bluez_device_org_bluez_proximity_reporter1_get_link_loss_alert_level (bluezdeviceOrgBluezProximityReporter1 *object); +gchar *bluez_device_org_bluez_proximity_reporter1_dup_link_loss_alert_level (bluezdeviceOrgBluezProximityReporter1 *object); +void bluez_device_org_bluez_proximity_reporter1_set_link_loss_alert_level (bluezdeviceOrgBluezProximityReporter1 *object, const gchar *value); + +const gchar *bluez_device_org_bluez_proximity_reporter1_get_immediate_alert_level (bluezdeviceOrgBluezProximityReporter1 *object); +gchar *bluez_device_org_bluez_proximity_reporter1_dup_immediate_alert_level (bluezdeviceOrgBluezProximityReporter1 *object); +void bluez_device_org_bluez_proximity_reporter1_set_immediate_alert_level (bluezdeviceOrgBluezProximityReporter1 *object, const gchar *value); + + +/* ---- */ + +#define BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY (bluez_device_org_bluez_proximity_reporter1_proxy_get_type ()) +#define BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY, bluezdeviceOrgBluezProximityReporter1Proxy)) +#define BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY, bluezdeviceOrgBluezProximityReporter1ProxyClass)) +#define BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY, bluezdeviceOrgBluezProximityReporter1ProxyClass)) +#define BLUEZ_DEVICE_IS_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY)) +#define BLUEZ_DEVICE_IS_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY)) + +typedef struct _bluezdeviceOrgBluezProximityReporter1Proxy bluezdeviceOrgBluezProximityReporter1Proxy; +typedef struct _bluezdeviceOrgBluezProximityReporter1ProxyClass bluezdeviceOrgBluezProximityReporter1ProxyClass; +typedef struct _bluezdeviceOrgBluezProximityReporter1ProxyPrivate bluezdeviceOrgBluezProximityReporter1ProxyPrivate; + +struct _bluezdeviceOrgBluezProximityReporter1Proxy +{ + /*< private >*/ + GDBusProxy parent_instance; + bluezdeviceOrgBluezProximityReporter1ProxyPrivate *priv; +}; + +struct _bluezdeviceOrgBluezProximityReporter1ProxyClass +{ + GDBusProxyClass parent_class; +}; + +GType bluez_device_org_bluez_proximity_reporter1_proxy_get_type (void) G_GNUC_CONST; + +void bluez_device_org_bluez_proximity_reporter1_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezdeviceOrgBluezProximityReporter1 *bluez_device_org_bluez_proximity_reporter1_proxy_new_finish ( + GAsyncResult *res, + GError **error); +bluezdeviceOrgBluezProximityReporter1 *bluez_device_org_bluez_proximity_reporter1_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + +void bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +bluezdeviceOrgBluezProximityReporter1 *bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error); +bluezdeviceOrgBluezProximityReporter1 *bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + + +/* ---- */ + +#define BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (bluez_device_org_bluez_proximity_reporter1_skeleton_get_type ()) +#define BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON, bluezdeviceOrgBluezProximityReporter1Skeleton)) +#define BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON, bluezdeviceOrgBluezProximityReporter1SkeletonClass)) +#define BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON, bluezdeviceOrgBluezProximityReporter1SkeletonClass)) +#define BLUEZ_DEVICE_IS_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON)) +#define BLUEZ_DEVICE_IS_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON)) + +typedef struct _bluezdeviceOrgBluezProximityReporter1Skeleton bluezdeviceOrgBluezProximityReporter1Skeleton; +typedef struct _bluezdeviceOrgBluezProximityReporter1SkeletonClass bluezdeviceOrgBluezProximityReporter1SkeletonClass; +typedef struct _bluezdeviceOrgBluezProximityReporter1SkeletonPrivate bluezdeviceOrgBluezProximityReporter1SkeletonPrivate; + +struct _bluezdeviceOrgBluezProximityReporter1Skeleton +{ + /*< private >*/ + GDBusInterfaceSkeleton parent_instance; + bluezdeviceOrgBluezProximityReporter1SkeletonPrivate *priv; +}; + +struct _bluezdeviceOrgBluezProximityReporter1SkeletonClass +{ + GDBusInterfaceSkeletonClass parent_class; +}; + +GType bluez_device_org_bluez_proximity_reporter1_skeleton_get_type (void) G_GNUC_CONST; + +bluezdeviceOrgBluezProximityReporter1 *bluez_device_org_bluez_proximity_reporter1_skeleton_new (void); + + +G_END_DECLS + +#endif /* __BLUEZ_DEVICE_H__ */ diff --git a/modules/ble/deps/linux/dbus-bluez/src/bluez_characteristic.c b/modules/ble/deps/linux/dbus-bluez/src/bluez_characteristic.c new file mode 100644 index 00000000..1a70ea1f --- /dev/null +++ b/modules/ble/deps/linux/dbus-bluez/src/bluez_characteristic.c @@ -0,0 +1,4698 @@ +/* + * Generated by gdbus-codegen 2.40.0. DO NOT EDIT. + * + * The license of this code is the same as for the source it was derived from. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "bluez_characteristic.h" + +#include +#ifdef G_OS_UNIX +# include +#endif + +typedef struct +{ + GDBusArgInfo parent_struct; + gboolean use_gvariant; +} _ExtendedGDBusArgInfo; + +typedef struct +{ + GDBusMethodInfo parent_struct; + const gchar *signal_name; + gboolean pass_fdlist; +} _ExtendedGDBusMethodInfo; + +typedef struct +{ + GDBusSignalInfo parent_struct; + const gchar *signal_name; +} _ExtendedGDBusSignalInfo; + +typedef struct +{ + GDBusPropertyInfo parent_struct; + const gchar *hyphen_name; + gboolean use_gvariant; +} _ExtendedGDBusPropertyInfo; + +typedef struct +{ + GDBusInterfaceInfo parent_struct; + const gchar *hyphen_name; +} _ExtendedGDBusInterfaceInfo; + +typedef struct +{ + const _ExtendedGDBusPropertyInfo *info; + guint prop_id; + GValue orig_value; /* the value before the change */ +} ChangedProperty; + +static void +_changed_property_free (ChangedProperty *data) +{ + g_value_unset (&data->orig_value); + g_free (data); +} + +static gboolean +_g_strv_equal0 (gchar **a, gchar **b) +{ + gboolean ret = FALSE; + guint n; + if (a == NULL && b == NULL) + { + ret = TRUE; + goto out; + } + if (a == NULL || b == NULL) + goto out; + if (g_strv_length (a) != g_strv_length (b)) + goto out; + for (n = 0; a[n] != NULL; n++) + if (g_strcmp0 (a[n], b[n]) != 0) + goto out; + ret = TRUE; +out: + return ret; +} + +static gboolean +_g_variant_equal0 (GVariant *a, GVariant *b) +{ + gboolean ret = FALSE; + if (a == NULL && b == NULL) + { + ret = TRUE; + goto out; + } + if (a == NULL || b == NULL) + goto out; + ret = g_variant_equal (a, b); +out: + return ret; +} + +G_GNUC_UNUSED static gboolean +_g_value_equal (const GValue *a, const GValue *b) +{ + gboolean ret = FALSE; + g_assert (G_VALUE_TYPE (a) == G_VALUE_TYPE (b)); + switch (G_VALUE_TYPE (a)) + { + case G_TYPE_BOOLEAN: + ret = (g_value_get_boolean (a) == g_value_get_boolean (b)); + break; + case G_TYPE_UCHAR: + ret = (g_value_get_uchar (a) == g_value_get_uchar (b)); + break; + case G_TYPE_INT: + ret = (g_value_get_int (a) == g_value_get_int (b)); + break; + case G_TYPE_UINT: + ret = (g_value_get_uint (a) == g_value_get_uint (b)); + break; + case G_TYPE_INT64: + ret = (g_value_get_int64 (a) == g_value_get_int64 (b)); + break; + case G_TYPE_UINT64: + ret = (g_value_get_uint64 (a) == g_value_get_uint64 (b)); + break; + case G_TYPE_DOUBLE: + { + /* Avoid -Wfloat-equal warnings by doing a direct bit compare */ + gdouble da = g_value_get_double (a); + gdouble db = g_value_get_double (b); + ret = memcmp (&da, &db, sizeof (gdouble)) == 0; + } + break; + case G_TYPE_STRING: + ret = (g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0); + break; + case G_TYPE_VARIANT: + ret = _g_variant_equal0 (g_value_get_variant (a), g_value_get_variant (b)); + break; + default: + if (G_VALUE_TYPE (a) == G_TYPE_STRV) + ret = _g_strv_equal0 (g_value_get_boxed (a), g_value_get_boxed (b)); + else + g_critical ("_g_value_equal() does not handle type %s", g_type_name (G_VALUE_TYPE (a))); + break; + } + return ret; +} + +/* ------------------------------------------------------------------------ + * Code for interface org.freedesktop.DBus.Introspectable + * ------------------------------------------------------------------------ + */ + +/** + * SECTION:bluezcharacteristicOrgFreedesktopDBusIntrospectable + * @title: bluezcharacteristicOrgFreedesktopDBusIntrospectable + * @short_description: Generated C code for the org.freedesktop.DBus.Introspectable D-Bus interface + * + * This section contains code for working with the org.freedesktop.DBus.Introspectable D-Bus interface in C. + */ + +/* ---- Introspection data for org.freedesktop.DBus.Introspectable ---- */ + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_introspectable_method_info_introspect_OUT_ARG_xml = +{ + { + -1, + (gchar *) "xml", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_characteristic_org_freedesktop_dbus_introspectable_method_info_introspect_OUT_ARG_pointers[] = +{ + &_bluez_characteristic_org_freedesktop_dbus_introspectable_method_info_introspect_OUT_ARG_xml, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_characteristic_org_freedesktop_dbus_introspectable_method_info_introspect = +{ + { + -1, + (gchar *) "Introspect", + NULL, + (GDBusArgInfo **) &_bluez_characteristic_org_freedesktop_dbus_introspectable_method_info_introspect_OUT_ARG_pointers, + NULL + }, + "handle-introspect", + FALSE +}; + +static const _ExtendedGDBusMethodInfo * const _bluez_characteristic_org_freedesktop_dbus_introspectable_method_info_pointers[] = +{ + &_bluez_characteristic_org_freedesktop_dbus_introspectable_method_info_introspect, + NULL +}; + +static const _ExtendedGDBusInterfaceInfo _bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info = +{ + { + -1, + (gchar *) "org.freedesktop.DBus.Introspectable", + (GDBusMethodInfo **) &_bluez_characteristic_org_freedesktop_dbus_introspectable_method_info_pointers, + NULL, + NULL, + NULL + }, + "org-freedesktop-dbus-introspectable", +}; + + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info: + * + * Gets a machine-readable description of the org.freedesktop.DBus.Introspectable D-Bus interface. + * + * Returns: (transfer none): A #GDBusInterfaceInfo. Do not free. + */ +GDBusInterfaceInfo * +bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info (void) +{ + return (GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info.parent_struct; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_override_properties: + * @klass: The class structure for a #GObject-derived class. + * @property_id_begin: The property id to assign to the first overridden property. + * + * Overrides all #GObject properties in the #bluezcharacteristicOrgFreedesktopDBusIntrospectable interface for a concrete class. + * The properties are overridden in the order they are defined. + * + * Returns: The last property id. + */ +guint +bluez_characteristic_org_freedesktop_dbus_introspectable_override_properties (GObjectClass *klass, guint property_id_begin) +{ + return property_id_begin - 1; +} + + + +/** + * bluezcharacteristicOrgFreedesktopDBusIntrospectable: + * + * Abstract interface type for the D-Bus interface org.freedesktop.DBus.Introspectable. + */ + +/** + * bluezcharacteristicOrgFreedesktopDBusIntrospectableIface: + * @parent_iface: The parent interface. + * @handle_introspect: Handler for the #bluezcharacteristicOrgFreedesktopDBusIntrospectable::handle-introspect signal. + * + * Virtual table for the D-Bus interface org.freedesktop.DBus.Introspectable. + */ + +typedef bluezcharacteristicOrgFreedesktopDBusIntrospectableIface bluezcharacteristicOrgFreedesktopDBusIntrospectableInterface; +G_DEFINE_INTERFACE (bluezcharacteristicOrgFreedesktopDBusIntrospectable, bluez_characteristic_org_freedesktop_dbus_introspectable, G_TYPE_OBJECT); + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_default_init (bluezcharacteristicOrgFreedesktopDBusIntrospectableIface *iface) +{ + /* GObject signals for incoming D-Bus method calls: */ + /** + * bluezcharacteristicOrgFreedesktopDBusIntrospectable::handle-introspect: + * @object: A #bluezcharacteristicOrgFreedesktopDBusIntrospectable. + * @invocation: A #GDBusMethodInvocation. + * + * Signal emitted when a remote caller is invoking the Introspect() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_characteristic_org_freedesktop_dbus_introspectable_complete_introspect() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-introspect", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezcharacteristicOrgFreedesktopDBusIntrospectableIface, handle_introspect), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 1, + G_TYPE_DBUS_METHOD_INVOCATION); + +} + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the Introspect() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect_finish() to get the result of the operation. + * + * See bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect_sync() for the synchronous, blocking version of this method. + */ +void +bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect ( + bluezcharacteristicOrgFreedesktopDBusIntrospectable *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "Introspect", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect_finish: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy. + * @out_xml: (out): Return location for return parameter or %NULL to ignore. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect_finish ( + bluezcharacteristicOrgFreedesktopDBusIntrospectable *proxy, + gchar **out_xml, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(s)", + out_xml); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect_sync: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy. + * @out_xml: (out): Return location for return parameter or %NULL to ignore. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the Introspect() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic_org_freedesktop_dbus_introspectable_call_introspect_sync ( + bluezcharacteristicOrgFreedesktopDBusIntrospectable *proxy, + gchar **out_xml, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "Introspect", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(s)", + out_xml); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_complete_introspect: + * @object: A #bluezcharacteristicOrgFreedesktopDBusIntrospectable. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * @xml: Parameter to return. + * + * Helper function used in service implementations to finish handling invocations of the Introspect() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_characteristic_org_freedesktop_dbus_introspectable_complete_introspect ( + bluezcharacteristicOrgFreedesktopDBusIntrospectable *object, + GDBusMethodInvocation *invocation, + const gchar *xml) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", + xml)); +} + +/* ------------------------------------------------------------------------ */ + +/** + * bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy: + * + * The #bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyClass: + * @parent_class: The parent class. + * + * Class structure for #bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy. + */ + +struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyPrivate +{ + GData *qdata; +}; + +static void bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_iface_init (bluezcharacteristicOrgFreedesktopDBusIntrospectableIface *iface); + +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy, bluez_characteristic_org_freedesktop_dbus_introspectable_proxy, G_TYPE_DBUS_PROXY, + G_ADD_PRIVATE (bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy) + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy, bluez_characteristic_org_freedesktop_dbus_introspectable_proxy, G_TYPE_DBUS_PROXY, + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_iface_init)); + +#endif +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_finalize (GObject *object) +{ + bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy *proxy = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY (object); + g_datalist_clear (&proxy->priv->qdata); + G_OBJECT_CLASS (bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_parent_class)->finalize (object); +} + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ +} + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ +} + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_g_signal (GDBusProxy *proxy, + const gchar *sender_name G_GNUC_UNUSED, + const gchar *signal_name, + GVariant *parameters) +{ + _ExtendedGDBusSignalInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint n; + guint signal_id; + info = (_ExtendedGDBusSignalInfo *) g_dbus_interface_info_lookup_signal ((GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info.parent_struct, signal_name); + if (info == NULL) + return; + num_params = g_variant_n_children (parameters); + paramv = g_new0 (GValue, num_params + 1); + g_value_init (¶mv[0], BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE); + g_value_set_object (¶mv[0], proxy); + g_variant_iter_init (&iter, parameters); + n = 1; + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.args[n - 1]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE); + g_signal_emitv (paramv, signal_id, 0, NULL); + for (n = 0; n < num_params + 1; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_g_properties_changed (GDBusProxy *_proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties) +{ + bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy *proxy = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY (_proxy); + guint n; + const gchar *key; + GVariantIter *iter; + _ExtendedGDBusPropertyInfo *info; + g_variant_get (changed_properties, "a{sv}", &iter); + while (g_variant_iter_next (iter, "{&sv}", &key, NULL)) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info.parent_struct, key); + g_datalist_remove_data (&proxy->priv->qdata, key); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } + g_variant_iter_free (iter); + for (n = 0; invalidated_properties[n] != NULL; n++) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info.parent_struct, invalidated_properties[n]); + g_datalist_remove_data (&proxy->priv->qdata, invalidated_properties[n]); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } +} + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_init (bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy *proxy) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + proxy->priv = bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_get_instance_private (proxy); +#else + proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyPrivate); +#endif + + g_dbus_proxy_set_interface_info (G_DBUS_PROXY (proxy), bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info ()); +} + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_class_init (bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyClass *klass) +{ + GObjectClass *gobject_class; + GDBusProxyClass *proxy_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_finalize; + gobject_class->get_property = bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_get_property; + gobject_class->set_property = bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_set_property; + + proxy_class = G_DBUS_PROXY_CLASS (klass); + proxy_class->g_signal = bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_g_signal; + proxy_class->g_properties_changed = bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_g_properties_changed; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezcharacteristicOrgFreedesktopDBusIntrospectableProxyPrivate)); +#endif +} + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_iface_init (bluezcharacteristicOrgFreedesktopDBusIntrospectableIface *iface) +{ +} + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Asynchronously creates a proxy for the D-Bus interface org.freedesktop.DBus.Introspectable. See g_dbus_proxy_new() for more details. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_finish() to get the result of the operation. + * + * See bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Introspectable", NULL); +} + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new(). + * + * Returns: (transfer full) (type bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristicOrgFreedesktopDBusIntrospectable * +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (ret); + else + return NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_sync: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Synchronously creates a proxy for the D-Bus interface org.freedesktop.DBus.Introspectable. See g_dbus_proxy_new_sync() for more details. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristicOrgFreedesktopDBusIntrospectable * +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Introspectable", NULL); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (ret); + else + return NULL; +} + + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Like bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new() but takes a #GBusType instead of a #GDBusConnection. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus_finish() to get the result of the operation. + * + * See bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Introspectable", NULL); +} + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus(). + * + * Returns: (transfer full) (type bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristicOrgFreedesktopDBusIntrospectable * +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (ret); + else + return NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus_sync: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Like bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_sync() but takes a #GBusType instead of a #GDBusConnection. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezcharacteristicOrgFreedesktopDBusIntrospectableProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristicOrgFreedesktopDBusIntrospectable * +bluez_characteristic_org_freedesktop_dbus_introspectable_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Introspectable", NULL); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (ret); + else + return NULL; +} + + +/* ------------------------------------------------------------------------ */ + +/** + * bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton: + * + * The #bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonClass: + * @parent_class: The parent class. + * + * Class structure for #bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton. + */ + +struct _bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonPrivate +{ + GValue *properties; + GList *changed_properties; + GSource *changed_properties_idle_source; + GMainContext *context; + GMutex lock; +}; + +static void +_bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_handle_method_call ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton *skeleton = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (user_data); + _ExtendedGDBusMethodInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint num_extra; + guint n; + guint signal_id; + GValue return_value = G_VALUE_INIT; + info = (_ExtendedGDBusMethodInfo *) g_dbus_method_invocation_get_method_info (invocation); + g_assert (info != NULL); + num_params = g_variant_n_children (parameters); + num_extra = info->pass_fdlist ? 3 : 2; paramv = g_new0 (GValue, num_params + num_extra); + n = 0; + g_value_init (¶mv[n], BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE); + g_value_set_object (¶mv[n++], skeleton); + g_value_init (¶mv[n], G_TYPE_DBUS_METHOD_INVOCATION); + g_value_set_object (¶mv[n++], invocation); + if (info->pass_fdlist) + { +#ifdef G_OS_UNIX + g_value_init (¶mv[n], G_TYPE_UNIX_FD_LIST); + g_value_set_object (¶mv[n++], g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation))); +#else + g_assert_not_reached (); +#endif + } + g_variant_iter_init (&iter, parameters); + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.in_args[n - num_extra]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE); + g_value_init (&return_value, G_TYPE_BOOLEAN); + g_signal_emitv (paramv, signal_id, 0, &return_value); + if (!g_value_get_boolean (&return_value)) + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method %s is not implemented on interface %s", method_name, interface_name); + g_value_unset (&return_value); + for (n = 0; n < num_params + num_extra; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static GVariant * +_bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_handle_get_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton *skeleton = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + GVariant *ret; + ret = NULL; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + g_value_init (&value, pspec->value_type); + g_object_get_property (G_OBJECT (skeleton), info->hyphen_name, &value); + ret = g_dbus_gvalue_to_gvariant (&value, G_VARIANT_TYPE (info->parent_struct.signature)); + g_value_unset (&value); + } + return ret; +} + +static gboolean +_bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_handle_set_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GVariant *variant, + GError **error, + gpointer user_data) +{ + bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton *skeleton = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + gboolean ret; + ret = FALSE; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + if (info->use_gvariant) + g_value_set_variant (&value, variant); + else + g_dbus_gvariant_to_gvalue (variant, &value); + g_object_set_property (G_OBJECT (skeleton), info->hyphen_name, &value); + g_value_unset (&value); + ret = TRUE; + } + return ret; +} + +static const GDBusInterfaceVTable _bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_vtable = +{ + _bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_handle_method_call, + _bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_handle_get_property, + _bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_handle_set_property, + {NULL} +}; + +static GDBusInterfaceInfo * +bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_info (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info (); +} + +static GDBusInterfaceVTable * +bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_vtable (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return (GDBusInterfaceVTable *) &_bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_vtable; +} + +static GVariant * +bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_properties (GDBusInterfaceSkeleton *_skeleton) +{ + bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton *skeleton = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (_skeleton); + + GVariantBuilder builder; + guint n; + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + if (_bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info.parent_struct.properties == NULL) + goto out; + for (n = 0; _bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info.parent_struct.properties[n] != NULL; n++) + { + GDBusPropertyInfo *info = _bluez_characteristic_org_freedesktop_dbus_introspectable_interface_info.parent_struct.properties[n]; + if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) + { + GVariant *value; + value = _bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_handle_get_property (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)), NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.freedesktop.DBus.Introspectable", info->name, NULL, skeleton); + if (value != NULL) + { + g_variant_take_ref (value); + g_variant_builder_add (&builder, "{sv}", info->name, value); + g_variant_unref (value); + } + } + } +out: + return g_variant_builder_end (&builder); +} + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_flush (GDBusInterfaceSkeleton *_skeleton) +{ +} + +static void bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_iface_init (bluezcharacteristicOrgFreedesktopDBusIntrospectableIface *iface); +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton, bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_ADD_PRIVATE (bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton) + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton, bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_iface_init)); + +#endif +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_finalize (GObject *object) +{ + bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton *skeleton = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (object); + g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free); + if (skeleton->priv->changed_properties_idle_source != NULL) + g_source_destroy (skeleton->priv->changed_properties_idle_source); + g_main_context_unref (skeleton->priv->context); + g_mutex_clear (&skeleton->priv->lock); + G_OBJECT_CLASS (bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_parent_class)->finalize (object); +} + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_init (bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton *skeleton) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + skeleton->priv = bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_get_instance_private (skeleton); +#else + skeleton->priv = G_TYPE_INSTANCE_GET_PRIVATE (skeleton, BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON, bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonPrivate); +#endif + + g_mutex_init (&skeleton->priv->lock); + skeleton->priv->context = g_main_context_ref_thread_default (); +} + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_class_init (bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonClass *klass) +{ + GObjectClass *gobject_class; + GDBusInterfaceSkeletonClass *skeleton_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_finalize; + + skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass); + skeleton_class->get_info = bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_info; + skeleton_class->get_properties = bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_properties; + skeleton_class->flush = bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_flush; + skeleton_class->get_vtable = bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_vtable; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeletonPrivate)); +#endif +} + +static void +bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_iface_init (bluezcharacteristicOrgFreedesktopDBusIntrospectableIface *iface) +{ +} + +/** + * bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_new: + * + * Creates a skeleton object for the D-Bus interface org.freedesktop.DBus.Introspectable. + * + * Returns: (transfer full) (type bluezcharacteristicOrgFreedesktopDBusIntrospectableSkeleton): The skeleton object. + */ +bluezcharacteristicOrgFreedesktopDBusIntrospectable * +bluez_characteristic_org_freedesktop_dbus_introspectable_skeleton_new (void) +{ + return BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (g_object_new (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON, NULL)); +} + +/* ------------------------------------------------------------------------ + * Code for interface org.bluez.GattCharacteristic1 + * ------------------------------------------------------------------------ + */ + +/** + * SECTION:bluezcharacteristic + * @title: bluezcharacteristic + * @short_description: Generated C code for the org.bluez.GattCharacteristic1 D-Bus interface + * + * This section contains code for working with the org.bluez.GattCharacteristic1 D-Bus interface in C. + */ + +/* ---- Introspection data for org.bluez.GattCharacteristic1 ---- */ + +static const _ExtendedGDBusArgInfo _bluez_characteristic__method_info_read_value_OUT_ARG_value = +{ + { + -1, + (gchar *) "value", + (gchar *) "ay", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_characteristic__method_info_read_value_OUT_ARG_pointers[] = +{ + &_bluez_characteristic__method_info_read_value_OUT_ARG_value, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_characteristic__method_info_read_value = +{ + { + -1, + (gchar *) "ReadValue", + NULL, + (GDBusArgInfo **) &_bluez_characteristic__method_info_read_value_OUT_ARG_pointers, + NULL + }, + "handle-read-value", + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_characteristic__method_info_write_value_IN_ARG_value = +{ + { + -1, + (gchar *) "value", + (gchar *) "ay", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_characteristic__method_info_write_value_IN_ARG_pointers[] = +{ + &_bluez_characteristic__method_info_write_value_IN_ARG_value, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_characteristic__method_info_write_value = +{ + { + -1, + (gchar *) "WriteValue", + (GDBusArgInfo **) &_bluez_characteristic__method_info_write_value_IN_ARG_pointers, + NULL, + NULL + }, + "handle-write-value", + FALSE +}; + +static const _ExtendedGDBusMethodInfo _bluez_characteristic__method_info_start_notify = +{ + { + -1, + (gchar *) "StartNotify", + NULL, + NULL, + NULL + }, + "handle-start-notify", + FALSE +}; + +static const _ExtendedGDBusMethodInfo _bluez_characteristic__method_info_stop_notify = +{ + { + -1, + (gchar *) "StopNotify", + NULL, + NULL, + NULL + }, + "handle-stop-notify", + FALSE +}; + +static const _ExtendedGDBusMethodInfo * const _bluez_characteristic__method_info_pointers[] = +{ + &_bluez_characteristic__method_info_read_value, + &_bluez_characteristic__method_info_write_value, + &_bluez_characteristic__method_info_start_notify, + &_bluez_characteristic__method_info_stop_notify, + NULL +}; + +static const _ExtendedGDBusPropertyInfo _bluez_characteristic__property_info_uuid = +{ + { + -1, + (gchar *) "UUID", + (gchar *) "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "uuid", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_characteristic__property_info_service = +{ + { + -1, + (gchar *) "Service", + (gchar *) "o", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "service", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_characteristic__property_info_value = +{ + { + -1, + (gchar *) "Value", + (gchar *) "ay", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "value", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_characteristic__property_info_notifying = +{ + { + -1, + (gchar *) "Notifying", + (gchar *) "b", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "notifying", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_characteristic__property_info_flags = +{ + { + -1, + (gchar *) "Flags", + (gchar *) "as", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "flags", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_characteristic__property_info_descriptors = +{ + { + -1, + (gchar *) "Descriptors", + (gchar *) "ao", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "descriptors", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo * const _bluez_characteristic__property_info_pointers[] = +{ + &_bluez_characteristic__property_info_uuid, + &_bluez_characteristic__property_info_service, + &_bluez_characteristic__property_info_value, + &_bluez_characteristic__property_info_notifying, + &_bluez_characteristic__property_info_flags, + &_bluez_characteristic__property_info_descriptors, + NULL +}; + +static const _ExtendedGDBusInterfaceInfo _bluez_characteristic__interface_info = +{ + { + -1, + (gchar *) "org.bluez.GattCharacteristic1", + (GDBusMethodInfo **) &_bluez_characteristic__method_info_pointers, + NULL, + (GDBusPropertyInfo **) &_bluez_characteristic__property_info_pointers, + NULL + }, + "", +}; + + +/** + * bluez_characteristic__interface_info: + * + * Gets a machine-readable description of the org.bluez.GattCharacteristic1 D-Bus interface. + * + * Returns: (transfer none): A #GDBusInterfaceInfo. Do not free. + */ +GDBusInterfaceInfo * +bluez_characteristic__interface_info (void) +{ + return (GDBusInterfaceInfo *) &_bluez_characteristic__interface_info.parent_struct; +} + +/** + * bluez_characteristic__override_properties: + * @klass: The class structure for a #GObject-derived class. + * @property_id_begin: The property id to assign to the first overridden property. + * + * Overrides all #GObject properties in the #bluezcharacteristic interface for a concrete class. + * The properties are overridden in the order they are defined. + * + * Returns: The last property id. + */ +guint +bluez_characteristic__override_properties (GObjectClass *klass, guint property_id_begin) +{ + g_object_class_override_property (klass, property_id_begin++, "uuid"); + g_object_class_override_property (klass, property_id_begin++, "service"); + g_object_class_override_property (klass, property_id_begin++, "value"); + g_object_class_override_property (klass, property_id_begin++, "notifying"); + g_object_class_override_property (klass, property_id_begin++, "flags"); + g_object_class_override_property (klass, property_id_begin++, "descriptors"); + return property_id_begin - 1; +} + + + +/** + * bluezcharacteristic: + * + * Abstract interface type for the D-Bus interface org.bluez.GattCharacteristic1. + */ + +/** + * bluezcharacteristicIface: + * @parent_iface: The parent interface. + * @handle_read_value: Handler for the #bluezcharacteristic::handle-read-value signal. + * @handle_start_notify: Handler for the #bluezcharacteristic::handle-start-notify signal. + * @handle_stop_notify: Handler for the #bluezcharacteristic::handle-stop-notify signal. + * @handle_write_value: Handler for the #bluezcharacteristic::handle-write-value signal. + * @get_descriptors: Getter for the #bluezcharacteristic:descriptors property. + * @get_flags: Getter for the #bluezcharacteristic:flags property. + * @get_notifying: Getter for the #bluezcharacteristic:notifying property. + * @get_service: Getter for the #bluezcharacteristic:service property. + * @get_uuid: Getter for the #bluezcharacteristic:uuid property. + * @get_value: Getter for the #bluezcharacteristic:value property. + * + * Virtual table for the D-Bus interface org.bluez.GattCharacteristic1. + */ + +typedef bluezcharacteristicIface bluezcharacteristicInterface; +G_DEFINE_INTERFACE (bluezcharacteristic, bluez_characteristic_, G_TYPE_OBJECT); + +static void +bluez_characteristic__default_init (bluezcharacteristicIface *iface) +{ + /* GObject signals for incoming D-Bus method calls: */ + /** + * bluezcharacteristic::handle-read-value: + * @object: A #bluezcharacteristic. + * @invocation: A #GDBusMethodInvocation. + * + * Signal emitted when a remote caller is invoking the ReadValue() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_characteristic__complete_read_value() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-read-value", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezcharacteristicIface, handle_read_value), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 1, + G_TYPE_DBUS_METHOD_INVOCATION); + + /** + * bluezcharacteristic::handle-write-value: + * @object: A #bluezcharacteristic. + * @invocation: A #GDBusMethodInvocation. + * @arg_value: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the WriteValue() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_characteristic__complete_write_value() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-write-value", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezcharacteristicIface, handle_write_value), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 2, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING); + + /** + * bluezcharacteristic::handle-start-notify: + * @object: A #bluezcharacteristic. + * @invocation: A #GDBusMethodInvocation. + * + * Signal emitted when a remote caller is invoking the StartNotify() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_characteristic__complete_start_notify() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-start-notify", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezcharacteristicIface, handle_start_notify), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 1, + G_TYPE_DBUS_METHOD_INVOCATION); + + /** + * bluezcharacteristic::handle-stop-notify: + * @object: A #bluezcharacteristic. + * @invocation: A #GDBusMethodInvocation. + * + * Signal emitted when a remote caller is invoking the StopNotify() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_characteristic__complete_stop_notify() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-stop-notify", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezcharacteristicIface, handle_stop_notify), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 1, + G_TYPE_DBUS_METHOD_INVOCATION); + + /* GObject properties for D-Bus properties: */ + /** + * bluezcharacteristic:uuid: + * + * Represents the D-Bus property "UUID". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("uuid", "UUID", "UUID", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezcharacteristic:service: + * + * Represents the D-Bus property "Service". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("service", "Service", "Service", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezcharacteristic:value: + * + * Represents the D-Bus property "Value". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("value", "Value", "Value", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezcharacteristic:notifying: + * + * Represents the D-Bus property "Notifying". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_boolean ("notifying", "Notifying", "Notifying", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezcharacteristic:flags: + * + * Represents the D-Bus property "Flags". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_boxed ("flags", "Flags", "Flags", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezcharacteristic:descriptors: + * + * Represents the D-Bus property "Descriptors". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_boxed ("descriptors", "Descriptors", "Descriptors", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +/** + * bluez_characteristic__get_uuid: (skip) + * @object: A #bluezcharacteristic. + * + * Gets the value of the "UUID" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_characteristic__dup_uuid() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar * +bluez_characteristic__get_uuid (bluezcharacteristic *object) +{ + return BLUEZ_CHARACTERISTIC__GET_IFACE (object)->get_uuid (object); +} + +/** + * bluez_characteristic__dup_uuid: (skip) + * @object: A #bluezcharacteristic. + * + * Gets a copy of the "UUID" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_free(). + */ +gchar * +bluez_characteristic__dup_uuid (bluezcharacteristic *object) +{ + gchar *value; + g_object_get (G_OBJECT (object), "uuid", &value, NULL); + return value; +} + +/** + * bluez_characteristic__set_uuid: (skip) + * @object: A #bluezcharacteristic. + * @value: The value to set. + * + * Sets the "UUID" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_characteristic__set_uuid (bluezcharacteristic *object, const gchar *value) +{ + g_object_set (G_OBJECT (object), "uuid", value, NULL); +} + +/** + * bluez_characteristic__get_service: (skip) + * @object: A #bluezcharacteristic. + * + * Gets the value of the "Service" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_characteristic__dup_service() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar * +bluez_characteristic__get_service (bluezcharacteristic *object) +{ + return BLUEZ_CHARACTERISTIC__GET_IFACE (object)->get_service (object); +} + +/** + * bluez_characteristic__dup_service: (skip) + * @object: A #bluezcharacteristic. + * + * Gets a copy of the "Service" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_free(). + */ +gchar * +bluez_characteristic__dup_service (bluezcharacteristic *object) +{ + gchar *value; + g_object_get (G_OBJECT (object), "service", &value, NULL); + return value; +} + +/** + * bluez_characteristic__set_service: (skip) + * @object: A #bluezcharacteristic. + * @value: The value to set. + * + * Sets the "Service" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_characteristic__set_service (bluezcharacteristic *object, const gchar *value) +{ + g_object_set (G_OBJECT (object), "service", value, NULL); +} + +/** + * bluez_characteristic__get_value: (skip) + * @object: A #bluezcharacteristic. + * + * Gets the value of the "Value" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_characteristic__dup_value() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar * +bluez_characteristic__get_value (bluezcharacteristic *object) +{ + return BLUEZ_CHARACTERISTIC__GET_IFACE (object)->get_value (object); +} + +/** + * bluez_characteristic__dup_value: (skip) + * @object: A #bluezcharacteristic. + * + * Gets a copy of the "Value" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_free(). + */ +gchar * +bluez_characteristic__dup_value (bluezcharacteristic *object) +{ + gchar *value; + g_object_get (G_OBJECT (object), "value", &value, NULL); + return value; +} + +/** + * bluez_characteristic__set_value: (skip) + * @object: A #bluezcharacteristic. + * @value: The value to set. + * + * Sets the "Value" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_characteristic__set_value (bluezcharacteristic *object, const gchar *value) +{ + g_object_set (G_OBJECT (object), "value", value, NULL); +} + +/** + * bluez_characteristic__get_notifying: (skip) + * @object: A #bluezcharacteristic. + * + * Gets the value of the "Notifying" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: The property value. + */ +gboolean +bluez_characteristic__get_notifying (bluezcharacteristic *object) +{ + return BLUEZ_CHARACTERISTIC__GET_IFACE (object)->get_notifying (object); +} + +/** + * bluez_characteristic__set_notifying: (skip) + * @object: A #bluezcharacteristic. + * @value: The value to set. + * + * Sets the "Notifying" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_characteristic__set_notifying (bluezcharacteristic *object, gboolean value) +{ + g_object_set (G_OBJECT (object), "notifying", value, NULL); +} + +/** + * bluez_characteristic__get_flags: (skip) + * @object: A #bluezcharacteristic. + * + * Gets the value of the "Flags" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_characteristic__dup_flags() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar *const * +bluez_characteristic__get_flags (bluezcharacteristic *object) +{ + return BLUEZ_CHARACTERISTIC__GET_IFACE (object)->get_flags (object); +} + +/** + * bluez_characteristic__dup_flags: (skip) + * @object: A #bluezcharacteristic. + * + * Gets a copy of the "Flags" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_strfreev(). + */ +gchar ** +bluez_characteristic__dup_flags (bluezcharacteristic *object) +{ + gchar **value; + g_object_get (G_OBJECT (object), "flags", &value, NULL); + return value; +} + +/** + * bluez_characteristic__set_flags: (skip) + * @object: A #bluezcharacteristic. + * @value: The value to set. + * + * Sets the "Flags" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_characteristic__set_flags (bluezcharacteristic *object, const gchar *const *value) +{ + g_object_set (G_OBJECT (object), "flags", value, NULL); +} + +/** + * bluez_characteristic__get_descriptors: (skip) + * @object: A #bluezcharacteristic. + * + * Gets the value of the "Descriptors" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_characteristic__dup_descriptors() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar *const * +bluez_characteristic__get_descriptors (bluezcharacteristic *object) +{ + return BLUEZ_CHARACTERISTIC__GET_IFACE (object)->get_descriptors (object); +} + +/** + * bluez_characteristic__dup_descriptors: (skip) + * @object: A #bluezcharacteristic. + * + * Gets a copy of the "Descriptors" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_strfreev(). + */ +gchar ** +bluez_characteristic__dup_descriptors (bluezcharacteristic *object) +{ + gchar **value; + g_object_get (G_OBJECT (object), "descriptors", &value, NULL); + return value; +} + +/** + * bluez_characteristic__set_descriptors: (skip) + * @object: A #bluezcharacteristic. + * @value: The value to set. + * + * Sets the "Descriptors" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_characteristic__set_descriptors (bluezcharacteristic *object, const gchar *const *value) +{ + g_object_set (G_OBJECT (object), "descriptors", value, NULL); +} + +/** + * bluez_characteristic__call_read_value: + * @proxy: A #bluezcharacteristicProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the ReadValue() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic__call_read_value_finish() to get the result of the operation. + * + * See bluez_characteristic__call_read_value_sync() for the synchronous, blocking version of this method. + */ +void +bluez_characteristic__call_read_value ( + bluezcharacteristic *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "ReadValue", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_characteristic__call_read_value_finish: + * @proxy: A #bluezcharacteristicProxy. + * @out_value: (out): Return location for return parameter or %NULL to ignore. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic__call_read_value(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_characteristic__call_read_value(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic__call_read_value_finish ( + bluezcharacteristic *proxy, + gchar **out_value, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(^ay)", + out_value); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic__call_read_value_sync: + * @proxy: A #bluezcharacteristicProxy. + * @out_value: (out): Return location for return parameter or %NULL to ignore. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the ReadValue() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_characteristic__call_read_value() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic__call_read_value_sync ( + bluezcharacteristic *proxy, + gchar **out_value, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "ReadValue", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(^ay)", + out_value); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic__call_write_value: + * @proxy: A #bluezcharacteristicProxy. + * @arg_value: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the WriteValue() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic__call_write_value_finish() to get the result of the operation. + * + * See bluez_characteristic__call_write_value_sync() for the synchronous, blocking version of this method. + */ +void +bluez_characteristic__call_write_value ( + bluezcharacteristic *proxy, + const gchar *arg_value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "WriteValue", + g_variant_new ("(^ay)", + arg_value), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_characteristic__call_write_value_finish: + * @proxy: A #bluezcharacteristicProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic__call_write_value(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_characteristic__call_write_value(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic__call_write_value_finish ( + bluezcharacteristic *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic__call_write_value_sync: + * @proxy: A #bluezcharacteristicProxy. + * @arg_value: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the WriteValue() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_characteristic__call_write_value() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic__call_write_value_sync ( + bluezcharacteristic *proxy, + const gchar *arg_value, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "WriteValue", + g_variant_new ("(^ay)", + arg_value), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic__call_start_notify: + * @proxy: A #bluezcharacteristicProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the StartNotify() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic__call_start_notify_finish() to get the result of the operation. + * + * See bluez_characteristic__call_start_notify_sync() for the synchronous, blocking version of this method. + */ +void +bluez_characteristic__call_start_notify ( + bluezcharacteristic *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "StartNotify", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_characteristic__call_start_notify_finish: + * @proxy: A #bluezcharacteristicProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic__call_start_notify(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_characteristic__call_start_notify(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic__call_start_notify_finish ( + bluezcharacteristic *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic__call_start_notify_sync: + * @proxy: A #bluezcharacteristicProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the StartNotify() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_characteristic__call_start_notify() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic__call_start_notify_sync ( + bluezcharacteristic *proxy, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "StartNotify", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic__call_stop_notify: + * @proxy: A #bluezcharacteristicProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the StopNotify() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic__call_stop_notify_finish() to get the result of the operation. + * + * See bluez_characteristic__call_stop_notify_sync() for the synchronous, blocking version of this method. + */ +void +bluez_characteristic__call_stop_notify ( + bluezcharacteristic *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "StopNotify", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_characteristic__call_stop_notify_finish: + * @proxy: A #bluezcharacteristicProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic__call_stop_notify(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_characteristic__call_stop_notify(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic__call_stop_notify_finish ( + bluezcharacteristic *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic__call_stop_notify_sync: + * @proxy: A #bluezcharacteristicProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the StopNotify() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_characteristic__call_stop_notify() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic__call_stop_notify_sync ( + bluezcharacteristic *proxy, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "StopNotify", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic__complete_read_value: + * @object: A #bluezcharacteristic. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * @value: Parameter to return. + * + * Helper function used in service implementations to finish handling invocations of the ReadValue() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_characteristic__complete_read_value ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation, + const gchar *value) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(^ay)", + value)); +} + +/** + * bluez_characteristic__complete_write_value: + * @object: A #bluezcharacteristic. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the WriteValue() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_characteristic__complete_write_value ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/** + * bluez_characteristic__complete_start_notify: + * @object: A #bluezcharacteristic. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the StartNotify() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_characteristic__complete_start_notify ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/** + * bluez_characteristic__complete_stop_notify: + * @object: A #bluezcharacteristic. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the StopNotify() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_characteristic__complete_stop_notify ( + bluezcharacteristic *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/* ------------------------------------------------------------------------ */ + +/** + * bluezcharacteristicProxy: + * + * The #bluezcharacteristicProxy structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezcharacteristicProxyClass: + * @parent_class: The parent class. + * + * Class structure for #bluezcharacteristicProxy. + */ + +struct _bluezcharacteristicProxyPrivate +{ + GData *qdata; +}; + +static void bluez_characteristic__proxy_iface_init (bluezcharacteristicIface *iface); + +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicProxy, bluez_characteristic__proxy, G_TYPE_DBUS_PROXY, + G_ADD_PRIVATE (bluezcharacteristicProxy) + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_, bluez_characteristic__proxy_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicProxy, bluez_characteristic__proxy, G_TYPE_DBUS_PROXY, + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_, bluez_characteristic__proxy_iface_init)); + +#endif +static void +bluez_characteristic__proxy_finalize (GObject *object) +{ + bluezcharacteristicProxy *proxy = BLUEZ_CHARACTERISTIC__PROXY (object); + g_datalist_clear (&proxy->priv->qdata); + G_OBJECT_CLASS (bluez_characteristic__proxy_parent_class)->finalize (object); +} + +static void +bluez_characteristic__proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ + const _ExtendedGDBusPropertyInfo *info; + GVariant *variant; + g_assert (prop_id != 0 && prop_id - 1 < 6); + info = _bluez_characteristic__property_info_pointers[prop_id - 1]; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (object), info->parent_struct.name); + if (info->use_gvariant) + { + g_value_set_variant (value, variant); + } + else + { + if (variant != NULL) + g_dbus_gvariant_to_gvalue (variant, value); + } + if (variant != NULL) + g_variant_unref (variant); +} + +static void +bluez_characteristic__proxy_set_property_cb (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + const _ExtendedGDBusPropertyInfo *info = user_data; + GError *error; + GVariant *_ret; + error = NULL; + _ret = g_dbus_proxy_call_finish (proxy, res, &error); + if (!_ret) + { + g_warning ("Error setting property '%s' on interface org.bluez.GattCharacteristic1: %s (%s, %d)", + info->parent_struct.name, + error->message, g_quark_to_string (error->domain), error->code); + g_error_free (error); + } + else + { + g_variant_unref (_ret); + } +} + +static void +bluez_characteristic__proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ + const _ExtendedGDBusPropertyInfo *info; + GVariant *variant; + g_assert (prop_id != 0 && prop_id - 1 < 6); + info = _bluez_characteristic__property_info_pointers[prop_id - 1]; + variant = g_dbus_gvalue_to_gvariant (value, G_VARIANT_TYPE (info->parent_struct.signature)); + g_dbus_proxy_call (G_DBUS_PROXY (object), + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", "org.bluez.GattCharacteristic1", info->parent_struct.name, variant), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, (GAsyncReadyCallback) bluez_characteristic__proxy_set_property_cb, (GDBusPropertyInfo *) &info->parent_struct); + g_variant_unref (variant); +} + +static void +bluez_characteristic__proxy_g_signal (GDBusProxy *proxy, + const gchar *sender_name G_GNUC_UNUSED, + const gchar *signal_name, + GVariant *parameters) +{ + _ExtendedGDBusSignalInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint n; + guint signal_id; + info = (_ExtendedGDBusSignalInfo *) g_dbus_interface_info_lookup_signal ((GDBusInterfaceInfo *) &_bluez_characteristic__interface_info.parent_struct, signal_name); + if (info == NULL) + return; + num_params = g_variant_n_children (parameters); + paramv = g_new0 (GValue, num_params + 1); + g_value_init (¶mv[0], BLUEZ_CHARACTERISTIC_TYPE_); + g_value_set_object (¶mv[0], proxy); + g_variant_iter_init (&iter, parameters); + n = 1; + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.args[n - 1]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_CHARACTERISTIC_TYPE_); + g_signal_emitv (paramv, signal_id, 0, NULL); + for (n = 0; n < num_params + 1; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static void +bluez_characteristic__proxy_g_properties_changed (GDBusProxy *_proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties) +{ + bluezcharacteristicProxy *proxy = BLUEZ_CHARACTERISTIC__PROXY (_proxy); + guint n; + const gchar *key; + GVariantIter *iter; + _ExtendedGDBusPropertyInfo *info; + g_variant_get (changed_properties, "a{sv}", &iter); + while (g_variant_iter_next (iter, "{&sv}", &key, NULL)) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic__interface_info.parent_struct, key); + g_datalist_remove_data (&proxy->priv->qdata, key); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } + g_variant_iter_free (iter); + for (n = 0; invalidated_properties[n] != NULL; n++) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic__interface_info.parent_struct, invalidated_properties[n]); + g_datalist_remove_data (&proxy->priv->qdata, invalidated_properties[n]); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } +} + +static const gchar * +bluez_characteristic__proxy_get_uuid (bluezcharacteristic *object) +{ + bluezcharacteristicProxy *proxy = BLUEZ_CHARACTERISTIC__PROXY (object); + GVariant *variant; + const gchar *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "UUID"); + if (variant != NULL) + { + value = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static const gchar * +bluez_characteristic__proxy_get_service (bluezcharacteristic *object) +{ + bluezcharacteristicProxy *proxy = BLUEZ_CHARACTERISTIC__PROXY (object); + GVariant *variant; + const gchar *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Service"); + if (variant != NULL) + { + value = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static const gchar * +bluez_characteristic__proxy_get_value (bluezcharacteristic *object) +{ + bluezcharacteristicProxy *proxy = BLUEZ_CHARACTERISTIC__PROXY (object); + GVariant *variant; + const gchar *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Value"); + if (variant != NULL) + { + value = g_variant_get_bytestring (variant); + g_variant_unref (variant); + } + return value; +} + +static gboolean +bluez_characteristic__proxy_get_notifying (bluezcharacteristic *object) +{ + bluezcharacteristicProxy *proxy = BLUEZ_CHARACTERISTIC__PROXY (object); + GVariant *variant; + gboolean value = 0; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Notifying"); + if (variant != NULL) + { + value = g_variant_get_boolean (variant); + g_variant_unref (variant); + } + return value; +} + +static const gchar *const * +bluez_characteristic__proxy_get_flags (bluezcharacteristic *object) +{ + bluezcharacteristicProxy *proxy = BLUEZ_CHARACTERISTIC__PROXY (object); + GVariant *variant; + const gchar *const *value = NULL; + value = g_datalist_get_data (&proxy->priv->qdata, "Flags"); + if (value != NULL) + return value; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Flags"); + if (variant != NULL) + { + value = g_variant_get_strv (variant, NULL); + g_datalist_set_data_full (&proxy->priv->qdata, "Flags", (gpointer) value, g_free); + g_variant_unref (variant); + } + return value; +} + +static const gchar *const * +bluez_characteristic__proxy_get_descriptors (bluezcharacteristic *object) +{ + bluezcharacteristicProxy *proxy = BLUEZ_CHARACTERISTIC__PROXY (object); + GVariant *variant; + const gchar *const *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Descriptors"); + if (variant != NULL) + { + value = g_variant_get_objv (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static void +bluez_characteristic__proxy_init (bluezcharacteristicProxy *proxy) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + proxy->priv = bluez_characteristic__proxy_get_instance_private (proxy); +#else + proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, BLUEZ_CHARACTERISTIC_TYPE__PROXY, bluezcharacteristicProxyPrivate); +#endif + + g_dbus_proxy_set_interface_info (G_DBUS_PROXY (proxy), bluez_characteristic__interface_info ()); +} + +static void +bluez_characteristic__proxy_class_init (bluezcharacteristicProxyClass *klass) +{ + GObjectClass *gobject_class; + GDBusProxyClass *proxy_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_characteristic__proxy_finalize; + gobject_class->get_property = bluez_characteristic__proxy_get_property; + gobject_class->set_property = bluez_characteristic__proxy_set_property; + + proxy_class = G_DBUS_PROXY_CLASS (klass); + proxy_class->g_signal = bluez_characteristic__proxy_g_signal; + proxy_class->g_properties_changed = bluez_characteristic__proxy_g_properties_changed; + + bluez_characteristic__override_properties (gobject_class, 1); + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezcharacteristicProxyPrivate)); +#endif +} + +static void +bluez_characteristic__proxy_iface_init (bluezcharacteristicIface *iface) +{ + iface->get_uuid = bluez_characteristic__proxy_get_uuid; + iface->get_service = bluez_characteristic__proxy_get_service; + iface->get_value = bluez_characteristic__proxy_get_value; + iface->get_notifying = bluez_characteristic__proxy_get_notifying; + iface->get_flags = bluez_characteristic__proxy_get_flags; + iface->get_descriptors = bluez_characteristic__proxy_get_descriptors; +} + +/** + * bluez_characteristic__proxy_new: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Asynchronously creates a proxy for the D-Bus interface org.bluez.GattCharacteristic1. See g_dbus_proxy_new() for more details. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic__proxy_new_finish() to get the result of the operation. + * + * See bluez_characteristic__proxy_new_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_characteristic__proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_CHARACTERISTIC_TYPE__PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.bluez.GattCharacteristic1", NULL); +} + +/** + * bluez_characteristic__proxy_new_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic__proxy_new(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_characteristic__proxy_new(). + * + * Returns: (transfer full) (type bluezcharacteristicProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristic * +bluez_characteristic__proxy_new_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ (ret); + else + return NULL; +} + +/** + * bluez_characteristic__proxy_new_sync: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Synchronously creates a proxy for the D-Bus interface org.bluez.GattCharacteristic1. See g_dbus_proxy_new_sync() for more details. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_characteristic__proxy_new() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezcharacteristicProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristic * +bluez_characteristic__proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_CHARACTERISTIC_TYPE__PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.bluez.GattCharacteristic1", NULL); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ (ret); + else + return NULL; +} + + +/** + * bluez_characteristic__proxy_new_for_bus: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Like bluez_characteristic__proxy_new() but takes a #GBusType instead of a #GDBusConnection. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic__proxy_new_for_bus_finish() to get the result of the operation. + * + * See bluez_characteristic__proxy_new_for_bus_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_characteristic__proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_CHARACTERISTIC_TYPE__PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.bluez.GattCharacteristic1", NULL); +} + +/** + * bluez_characteristic__proxy_new_for_bus_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic__proxy_new_for_bus(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_characteristic__proxy_new_for_bus(). + * + * Returns: (transfer full) (type bluezcharacteristicProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristic * +bluez_characteristic__proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ (ret); + else + return NULL; +} + +/** + * bluez_characteristic__proxy_new_for_bus_sync: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Like bluez_characteristic__proxy_new_sync() but takes a #GBusType instead of a #GDBusConnection. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_characteristic__proxy_new_for_bus() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezcharacteristicProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristic * +bluez_characteristic__proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_CHARACTERISTIC_TYPE__PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.bluez.GattCharacteristic1", NULL); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ (ret); + else + return NULL; +} + + +/* ------------------------------------------------------------------------ */ + +/** + * bluezcharacteristicSkeleton: + * + * The #bluezcharacteristicSkeleton structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezcharacteristicSkeletonClass: + * @parent_class: The parent class. + * + * Class structure for #bluezcharacteristicSkeleton. + */ + +struct _bluezcharacteristicSkeletonPrivate +{ + GValue *properties; + GList *changed_properties; + GSource *changed_properties_idle_source; + GMainContext *context; + GMutex lock; +}; + +static void +_bluez_characteristic__skeleton_handle_method_call ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (user_data); + _ExtendedGDBusMethodInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint num_extra; + guint n; + guint signal_id; + GValue return_value = G_VALUE_INIT; + info = (_ExtendedGDBusMethodInfo *) g_dbus_method_invocation_get_method_info (invocation); + g_assert (info != NULL); + num_params = g_variant_n_children (parameters); + num_extra = info->pass_fdlist ? 3 : 2; paramv = g_new0 (GValue, num_params + num_extra); + n = 0; + g_value_init (¶mv[n], BLUEZ_CHARACTERISTIC_TYPE_); + g_value_set_object (¶mv[n++], skeleton); + g_value_init (¶mv[n], G_TYPE_DBUS_METHOD_INVOCATION); + g_value_set_object (¶mv[n++], invocation); + if (info->pass_fdlist) + { +#ifdef G_OS_UNIX + g_value_init (¶mv[n], G_TYPE_UNIX_FD_LIST); + g_value_set_object (¶mv[n++], g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation))); +#else + g_assert_not_reached (); +#endif + } + g_variant_iter_init (&iter, parameters); + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.in_args[n - num_extra]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_CHARACTERISTIC_TYPE_); + g_value_init (&return_value, G_TYPE_BOOLEAN); + g_signal_emitv (paramv, signal_id, 0, &return_value); + if (!g_value_get_boolean (&return_value)) + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method %s is not implemented on interface %s", method_name, interface_name); + g_value_unset (&return_value); + for (n = 0; n < num_params + num_extra; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static GVariant * +_bluez_characteristic__skeleton_handle_get_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + GVariant *ret; + ret = NULL; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic__interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + g_value_init (&value, pspec->value_type); + g_object_get_property (G_OBJECT (skeleton), info->hyphen_name, &value); + ret = g_dbus_gvalue_to_gvariant (&value, G_VARIANT_TYPE (info->parent_struct.signature)); + g_value_unset (&value); + } + return ret; +} + +static gboolean +_bluez_characteristic__skeleton_handle_set_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GVariant *variant, + GError **error, + gpointer user_data) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + gboolean ret; + ret = FALSE; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic__interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + if (info->use_gvariant) + g_value_set_variant (&value, variant); + else + g_dbus_gvariant_to_gvalue (variant, &value); + g_object_set_property (G_OBJECT (skeleton), info->hyphen_name, &value); + g_value_unset (&value); + ret = TRUE; + } + return ret; +} + +static const GDBusInterfaceVTable _bluez_characteristic__skeleton_vtable = +{ + _bluez_characteristic__skeleton_handle_method_call, + _bluez_characteristic__skeleton_handle_get_property, + _bluez_characteristic__skeleton_handle_set_property, + {NULL} +}; + +static GDBusInterfaceInfo * +bluez_characteristic__skeleton_dbus_interface_get_info (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return bluez_characteristic__interface_info (); +} + +static GDBusInterfaceVTable * +bluez_characteristic__skeleton_dbus_interface_get_vtable (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return (GDBusInterfaceVTable *) &_bluez_characteristic__skeleton_vtable; +} + +static GVariant * +bluez_characteristic__skeleton_dbus_interface_get_properties (GDBusInterfaceSkeleton *_skeleton) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (_skeleton); + + GVariantBuilder builder; + guint n; + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + if (_bluez_characteristic__interface_info.parent_struct.properties == NULL) + goto out; + for (n = 0; _bluez_characteristic__interface_info.parent_struct.properties[n] != NULL; n++) + { + GDBusPropertyInfo *info = _bluez_characteristic__interface_info.parent_struct.properties[n]; + if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) + { + GVariant *value; + value = _bluez_characteristic__skeleton_handle_get_property (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)), NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.bluez.GattCharacteristic1", info->name, NULL, skeleton); + if (value != NULL) + { + g_variant_take_ref (value); + g_variant_builder_add (&builder, "{sv}", info->name, value); + g_variant_unref (value); + } + } + } +out: + return g_variant_builder_end (&builder); +} + +static gboolean _bluez_characteristic__emit_changed (gpointer user_data); + +static void +bluez_characteristic__skeleton_dbus_interface_flush (GDBusInterfaceSkeleton *_skeleton) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (_skeleton); + gboolean emit_changed = FALSE; + + g_mutex_lock (&skeleton->priv->lock); + if (skeleton->priv->changed_properties_idle_source != NULL) + { + g_source_destroy (skeleton->priv->changed_properties_idle_source); + skeleton->priv->changed_properties_idle_source = NULL; + emit_changed = TRUE; + } + g_mutex_unlock (&skeleton->priv->lock); + + if (emit_changed) + _bluez_characteristic__emit_changed (skeleton); +} + +static void bluez_characteristic__skeleton_iface_init (bluezcharacteristicIface *iface); +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicSkeleton, bluez_characteristic__skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_ADD_PRIVATE (bluezcharacteristicSkeleton) + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_, bluez_characteristic__skeleton_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicSkeleton, bluez_characteristic__skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_, bluez_characteristic__skeleton_iface_init)); + +#endif +static void +bluez_characteristic__skeleton_finalize (GObject *object) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (object); + guint n; + for (n = 0; n < 6; n++) + g_value_unset (&skeleton->priv->properties[n]); + g_free (skeleton->priv->properties); + g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free); + if (skeleton->priv->changed_properties_idle_source != NULL) + g_source_destroy (skeleton->priv->changed_properties_idle_source); + g_main_context_unref (skeleton->priv->context); + g_mutex_clear (&skeleton->priv->lock); + G_OBJECT_CLASS (bluez_characteristic__skeleton_parent_class)->finalize (object); +} + +static void +bluez_characteristic__skeleton_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (object); + g_assert (prop_id != 0 && prop_id - 1 < 6); + g_mutex_lock (&skeleton->priv->lock); + g_value_copy (&skeleton->priv->properties[prop_id - 1], value); + g_mutex_unlock (&skeleton->priv->lock); +} + +static gboolean +_bluez_characteristic__emit_changed (gpointer user_data) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (user_data); + GList *l; + GVariantBuilder builder; + GVariantBuilder invalidated_builder; + guint num_changes; + + g_mutex_lock (&skeleton->priv->lock); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as")); + for (l = skeleton->priv->changed_properties, num_changes = 0; l != NULL; l = l->next) + { + ChangedProperty *cp = l->data; + GVariant *variant; + const GValue *cur_value; + + cur_value = &skeleton->priv->properties[cp->prop_id - 1]; + if (!_g_value_equal (cur_value, &cp->orig_value)) + { + variant = g_dbus_gvalue_to_gvariant (cur_value, G_VARIANT_TYPE (cp->info->parent_struct.signature)); + g_variant_builder_add (&builder, "{sv}", cp->info->parent_struct.name, variant); + g_variant_unref (variant); + num_changes++; + } + } + if (num_changes > 0) + { + GList *connections, *ll; + GVariant *signal_variant; + signal_variant = g_variant_ref_sink (g_variant_new ("(sa{sv}as)", "org.bluez.GattCharacteristic1", + &builder, &invalidated_builder)); + connections = g_dbus_interface_skeleton_get_connections (G_DBUS_INTERFACE_SKELETON (skeleton)); + for (ll = connections; ll != NULL; ll = ll->next) + { + GDBusConnection *connection = ll->data; + + g_dbus_connection_emit_signal (connection, + NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + signal_variant, + NULL); + } + g_variant_unref (signal_variant); + g_list_free_full (connections, g_object_unref); + } + else + { + g_variant_builder_clear (&builder); + g_variant_builder_clear (&invalidated_builder); + } + g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free); + skeleton->priv->changed_properties = NULL; + skeleton->priv->changed_properties_idle_source = NULL; + g_mutex_unlock (&skeleton->priv->lock); + return FALSE; +} + +static void +_bluez_characteristic__schedule_emit_changed (bluezcharacteristicSkeleton *skeleton, const _ExtendedGDBusPropertyInfo *info, guint prop_id, const GValue *orig_value) +{ + ChangedProperty *cp; + GList *l; + cp = NULL; + for (l = skeleton->priv->changed_properties; l != NULL; l = l->next) + { + ChangedProperty *i_cp = l->data; + if (i_cp->info == info) + { + cp = i_cp; + break; + } + } + if (cp == NULL) + { + cp = g_new0 (ChangedProperty, 1); + cp->prop_id = prop_id; + cp->info = info; + skeleton->priv->changed_properties = g_list_prepend (skeleton->priv->changed_properties, cp); + g_value_init (&cp->orig_value, G_VALUE_TYPE (orig_value)); + g_value_copy (orig_value, &cp->orig_value); + } +} + +static void +bluez_characteristic__skeleton_notify (GObject *object, + GParamSpec *pspec G_GNUC_UNUSED) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (object); + g_mutex_lock (&skeleton->priv->lock); + if (skeleton->priv->changed_properties != NULL && + skeleton->priv->changed_properties_idle_source == NULL) + { + skeleton->priv->changed_properties_idle_source = g_idle_source_new (); + g_source_set_priority (skeleton->priv->changed_properties_idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (skeleton->priv->changed_properties_idle_source, _bluez_characteristic__emit_changed, g_object_ref (skeleton), (GDestroyNotify) g_object_unref); + g_source_attach (skeleton->priv->changed_properties_idle_source, skeleton->priv->context); + g_source_unref (skeleton->priv->changed_properties_idle_source); + } + g_mutex_unlock (&skeleton->priv->lock); +} + +static void +bluez_characteristic__skeleton_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (object); + g_assert (prop_id != 0 && prop_id - 1 < 6); + g_mutex_lock (&skeleton->priv->lock); + g_object_freeze_notify (object); + if (!_g_value_equal (value, &skeleton->priv->properties[prop_id - 1])) + { + if (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)) != NULL) + _bluez_characteristic__schedule_emit_changed (skeleton, _bluez_characteristic__property_info_pointers[prop_id - 1], prop_id, &skeleton->priv->properties[prop_id - 1]); + g_value_copy (value, &skeleton->priv->properties[prop_id - 1]); + g_object_notify_by_pspec (object, pspec); + } + g_mutex_unlock (&skeleton->priv->lock); + g_object_thaw_notify (object); +} + +static void +bluez_characteristic__skeleton_init (bluezcharacteristicSkeleton *skeleton) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + skeleton->priv = bluez_characteristic__skeleton_get_instance_private (skeleton); +#else + skeleton->priv = G_TYPE_INSTANCE_GET_PRIVATE (skeleton, BLUEZ_CHARACTERISTIC_TYPE__SKELETON, bluezcharacteristicSkeletonPrivate); +#endif + + g_mutex_init (&skeleton->priv->lock); + skeleton->priv->context = g_main_context_ref_thread_default (); + skeleton->priv->properties = g_new0 (GValue, 6); + g_value_init (&skeleton->priv->properties[0], G_TYPE_STRING); + g_value_init (&skeleton->priv->properties[1], G_TYPE_STRING); + g_value_init (&skeleton->priv->properties[2], G_TYPE_STRING); + g_value_init (&skeleton->priv->properties[3], G_TYPE_BOOLEAN); + g_value_init (&skeleton->priv->properties[4], G_TYPE_STRV); + g_value_init (&skeleton->priv->properties[5], G_TYPE_STRV); +} + +static const gchar * +bluez_characteristic__skeleton_get_uuid (bluezcharacteristic *object) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (object); + const gchar *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_string (&(skeleton->priv->properties[0])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar * +bluez_characteristic__skeleton_get_service (bluezcharacteristic *object) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (object); + const gchar *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_string (&(skeleton->priv->properties[1])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar * +bluez_characteristic__skeleton_get_value (bluezcharacteristic *object) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (object); + const gchar *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_string (&(skeleton->priv->properties[2])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static gboolean +bluez_characteristic__skeleton_get_notifying (bluezcharacteristic *object) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (object); + gboolean value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_boolean (&(skeleton->priv->properties[3])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar *const * +bluez_characteristic__skeleton_get_flags (bluezcharacteristic *object) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (object); + const gchar *const *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_boxed (&(skeleton->priv->properties[4])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar *const * +bluez_characteristic__skeleton_get_descriptors (bluezcharacteristic *object) +{ + bluezcharacteristicSkeleton *skeleton = BLUEZ_CHARACTERISTIC__SKELETON (object); + const gchar *const *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_boxed (&(skeleton->priv->properties[5])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static void +bluez_characteristic__skeleton_class_init (bluezcharacteristicSkeletonClass *klass) +{ + GObjectClass *gobject_class; + GDBusInterfaceSkeletonClass *skeleton_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_characteristic__skeleton_finalize; + gobject_class->get_property = bluez_characteristic__skeleton_get_property; + gobject_class->set_property = bluez_characteristic__skeleton_set_property; + gobject_class->notify = bluez_characteristic__skeleton_notify; + + + bluez_characteristic__override_properties (gobject_class, 1); + + skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass); + skeleton_class->get_info = bluez_characteristic__skeleton_dbus_interface_get_info; + skeleton_class->get_properties = bluez_characteristic__skeleton_dbus_interface_get_properties; + skeleton_class->flush = bluez_characteristic__skeleton_dbus_interface_flush; + skeleton_class->get_vtable = bluez_characteristic__skeleton_dbus_interface_get_vtable; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezcharacteristicSkeletonPrivate)); +#endif +} + +static void +bluez_characteristic__skeleton_iface_init (bluezcharacteristicIface *iface) +{ + iface->get_uuid = bluez_characteristic__skeleton_get_uuid; + iface->get_service = bluez_characteristic__skeleton_get_service; + iface->get_value = bluez_characteristic__skeleton_get_value; + iface->get_notifying = bluez_characteristic__skeleton_get_notifying; + iface->get_flags = bluez_characteristic__skeleton_get_flags; + iface->get_descriptors = bluez_characteristic__skeleton_get_descriptors; +} + +/** + * bluez_characteristic__skeleton_new: + * + * Creates a skeleton object for the D-Bus interface org.bluez.GattCharacteristic1. + * + * Returns: (transfer full) (type bluezcharacteristicSkeleton): The skeleton object. + */ +bluezcharacteristic * +bluez_characteristic__skeleton_new (void) +{ + return BLUEZ_CHARACTERISTIC_ (g_object_new (BLUEZ_CHARACTERISTIC_TYPE__SKELETON, NULL)); +} + +/* ------------------------------------------------------------------------ + * Code for interface org.freedesktop.DBus.Properties + * ------------------------------------------------------------------------ + */ + +/** + * SECTION:bluezcharacteristicOrgFreedesktopDBusProperties + * @title: bluezcharacteristicOrgFreedesktopDBusProperties + * @short_description: Generated C code for the org.freedesktop.DBus.Properties D-Bus interface + * + * This section contains code for working with the org.freedesktop.DBus.Properties D-Bus interface in C. + */ + +/* ---- Introspection data for org.freedesktop.DBus.Properties ---- */ + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_IN_ARG_interface = +{ + { + -1, + (gchar *) "interface", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_IN_ARG_name = +{ + { + -1, + (gchar *) "name", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_IN_ARG_pointers[] = +{ + &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_IN_ARG_interface, + &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_IN_ARG_name, + NULL +}; + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_OUT_ARG_value = +{ + { + -1, + (gchar *) "value", + (gchar *) "v", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_OUT_ARG_pointers[] = +{ + &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_OUT_ARG_value, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_characteristic_org_freedesktop_dbus_properties_method_info_get = +{ + { + -1, + (gchar *) "Get", + (GDBusArgInfo **) &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_IN_ARG_pointers, + (GDBusArgInfo **) &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_OUT_ARG_pointers, + NULL + }, + "handle-get", + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_properties_method_info_set_IN_ARG_interface = +{ + { + -1, + (gchar *) "interface", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_properties_method_info_set_IN_ARG_name = +{ + { + -1, + (gchar *) "name", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_properties_method_info_set_IN_ARG_value = +{ + { + -1, + (gchar *) "value", + (gchar *) "v", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_characteristic_org_freedesktop_dbus_properties_method_info_set_IN_ARG_pointers[] = +{ + &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_set_IN_ARG_interface, + &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_set_IN_ARG_name, + &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_set_IN_ARG_value, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_characteristic_org_freedesktop_dbus_properties_method_info_set = +{ + { + -1, + (gchar *) "Set", + (GDBusArgInfo **) &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_set_IN_ARG_pointers, + NULL, + NULL + }, + "handle-set", + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_all_IN_ARG_interface = +{ + { + -1, + (gchar *) "interface", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_all_IN_ARG_pointers[] = +{ + &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_all_IN_ARG_interface, + NULL +}; + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_all_OUT_ARG_properties = +{ + { + -1, + (gchar *) "properties", + (gchar *) "a{sv}", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_all_OUT_ARG_pointers[] = +{ + &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_all_OUT_ARG_properties, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_all = +{ + { + -1, + (gchar *) "GetAll", + (GDBusArgInfo **) &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_all_IN_ARG_pointers, + (GDBusArgInfo **) &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_all_OUT_ARG_pointers, + NULL + }, + "handle-get-all", + FALSE +}; + +static const _ExtendedGDBusMethodInfo * const _bluez_characteristic_org_freedesktop_dbus_properties_method_info_pointers[] = +{ + &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_get, + &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_set, + &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_get_all, + NULL +}; + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_interface = +{ + { + -1, + (gchar *) "interface", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_changed_properties = +{ + { + -1, + (gchar *) "changed_properties", + (gchar *) "a{sv}", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_characteristic_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_invalidated_properties = +{ + { + -1, + (gchar *) "invalidated_properties", + (gchar *) "as", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_characteristic_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_pointers[] = +{ + &_bluez_characteristic_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_interface, + &_bluez_characteristic_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_changed_properties, + &_bluez_characteristic_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_invalidated_properties, + NULL +}; + +static const _ExtendedGDBusSignalInfo _bluez_characteristic_org_freedesktop_dbus_properties_signal_info_properties_changed = +{ + { + -1, + (gchar *) "PropertiesChanged", + (GDBusArgInfo **) &_bluez_characteristic_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_pointers, + NULL + }, + "properties-changed" +}; + +static const _ExtendedGDBusSignalInfo * const _bluez_characteristic_org_freedesktop_dbus_properties_signal_info_pointers[] = +{ + &_bluez_characteristic_org_freedesktop_dbus_properties_signal_info_properties_changed, + NULL +}; + +static const _ExtendedGDBusInterfaceInfo _bluez_characteristic_org_freedesktop_dbus_properties_interface_info = +{ + { + -1, + (gchar *) "org.freedesktop.DBus.Properties", + (GDBusMethodInfo **) &_bluez_characteristic_org_freedesktop_dbus_properties_method_info_pointers, + (GDBusSignalInfo **) &_bluez_characteristic_org_freedesktop_dbus_properties_signal_info_pointers, + NULL, + NULL + }, + "org-freedesktop-dbus-properties", +}; + + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_interface_info: + * + * Gets a machine-readable description of the org.freedesktop.DBus.Properties D-Bus interface. + * + * Returns: (transfer none): A #GDBusInterfaceInfo. Do not free. + */ +GDBusInterfaceInfo * +bluez_characteristic_org_freedesktop_dbus_properties_interface_info (void) +{ + return (GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_properties_interface_info.parent_struct; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_override_properties: + * @klass: The class structure for a #GObject-derived class. + * @property_id_begin: The property id to assign to the first overridden property. + * + * Overrides all #GObject properties in the #bluezcharacteristicOrgFreedesktopDBusProperties interface for a concrete class. + * The properties are overridden in the order they are defined. + * + * Returns: The last property id. + */ +guint +bluez_characteristic_org_freedesktop_dbus_properties_override_properties (GObjectClass *klass, guint property_id_begin) +{ + return property_id_begin - 1; +} + + + +/** + * bluezcharacteristicOrgFreedesktopDBusProperties: + * + * Abstract interface type for the D-Bus interface org.freedesktop.DBus.Properties. + */ + +/** + * bluezcharacteristicOrgFreedesktopDBusPropertiesIface: + * @parent_iface: The parent interface. + * @handle_get: Handler for the #bluezcharacteristicOrgFreedesktopDBusProperties::handle-get signal. + * @handle_get_all: Handler for the #bluezcharacteristicOrgFreedesktopDBusProperties::handle-get-all signal. + * @handle_set: Handler for the #bluezcharacteristicOrgFreedesktopDBusProperties::handle-set signal. + * @properties_changed: Handler for the #bluezcharacteristicOrgFreedesktopDBusProperties::properties-changed signal. + * + * Virtual table for the D-Bus interface org.freedesktop.DBus.Properties. + */ + +typedef bluezcharacteristicOrgFreedesktopDBusPropertiesIface bluezcharacteristicOrgFreedesktopDBusPropertiesInterface; +G_DEFINE_INTERFACE (bluezcharacteristicOrgFreedesktopDBusProperties, bluez_characteristic_org_freedesktop_dbus_properties, G_TYPE_OBJECT); + +static void +bluez_characteristic_org_freedesktop_dbus_properties_default_init (bluezcharacteristicOrgFreedesktopDBusPropertiesIface *iface) +{ + /* GObject signals for incoming D-Bus method calls: */ + /** + * bluezcharacteristicOrgFreedesktopDBusProperties::handle-get: + * @object: A #bluezcharacteristicOrgFreedesktopDBusProperties. + * @invocation: A #GDBusMethodInvocation. + * @arg_interface: Argument passed by remote caller. + * @arg_name: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the Get() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_characteristic_org_freedesktop_dbus_properties_complete_get() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-get", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezcharacteristicOrgFreedesktopDBusPropertiesIface, handle_get), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 3, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING, G_TYPE_STRING); + + /** + * bluezcharacteristicOrgFreedesktopDBusProperties::handle-set: + * @object: A #bluezcharacteristicOrgFreedesktopDBusProperties. + * @invocation: A #GDBusMethodInvocation. + * @arg_interface: Argument passed by remote caller. + * @arg_name: Argument passed by remote caller. + * @arg_value: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the Set() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_characteristic_org_freedesktop_dbus_properties_complete_set() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-set", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezcharacteristicOrgFreedesktopDBusPropertiesIface, handle_set), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 4, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_VARIANT); + + /** + * bluezcharacteristicOrgFreedesktopDBusProperties::handle-get-all: + * @object: A #bluezcharacteristicOrgFreedesktopDBusProperties. + * @invocation: A #GDBusMethodInvocation. + * @arg_interface: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the GetAll() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_characteristic_org_freedesktop_dbus_properties_complete_get_all() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-get-all", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezcharacteristicOrgFreedesktopDBusPropertiesIface, handle_get_all), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 2, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING); + + /* GObject signals for received D-Bus signals: */ + /** + * bluezcharacteristicOrgFreedesktopDBusProperties::properties-changed: + * @object: A #bluezcharacteristicOrgFreedesktopDBusProperties. + * @arg_interface: Argument. + * @arg_changed_properties: Argument. + * @arg_invalidated_properties: Argument. + * + * On the client-side, this signal is emitted whenever the D-Bus signal "PropertiesChanged" is received. + * + * On the service-side, this signal can be used with e.g. g_signal_emit_by_name() to make the object emit the D-Bus signal. + */ + g_signal_new ("properties-changed", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezcharacteristicOrgFreedesktopDBusPropertiesIface, properties_changed), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 3, G_TYPE_STRING, G_TYPE_VARIANT, G_TYPE_STRV); + +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_emit_properties_changed: + * @object: A #bluezcharacteristicOrgFreedesktopDBusProperties. + * @arg_interface: Argument to pass with the signal. + * @arg_changed_properties: Argument to pass with the signal. + * @arg_invalidated_properties: Argument to pass with the signal. + * + * Emits the "PropertiesChanged" D-Bus signal. + */ +void +bluez_characteristic_org_freedesktop_dbus_properties_emit_properties_changed ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + const gchar *arg_interface, + GVariant *arg_changed_properties, + const gchar *const *arg_invalidated_properties) +{ + g_signal_emit_by_name (object, "properties-changed", arg_interface, arg_changed_properties, arg_invalidated_properties); +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_call_get: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @arg_name: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the Get() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic_org_freedesktop_dbus_properties_call_get_finish() to get the result of the operation. + * + * See bluez_characteristic_org_freedesktop_dbus_properties_call_get_sync() for the synchronous, blocking version of this method. + */ +void +bluez_characteristic_org_freedesktop_dbus_properties_call_get ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "Get", + g_variant_new ("(ss)", + arg_interface, + arg_name), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_call_get_finish: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusPropertiesProxy. + * @out_value: (out): Return location for return parameter or %NULL to ignore. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic_org_freedesktop_dbus_properties_call_get(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_characteristic_org_freedesktop_dbus_properties_call_get(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic_org_freedesktop_dbus_properties_call_get_finish ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + GVariant **out_value, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(@v)", + out_value); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_call_get_sync: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @arg_name: Argument to pass with the method invocation. + * @out_value: (out): Return location for return parameter or %NULL to ignore. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the Get() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_characteristic_org_freedesktop_dbus_properties_call_get() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic_org_freedesktop_dbus_properties_call_get_sync ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant **out_value, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "Get", + g_variant_new ("(ss)", + arg_interface, + arg_name), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(@v)", + out_value); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_call_set: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @arg_name: Argument to pass with the method invocation. + * @arg_value: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the Set() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic_org_freedesktop_dbus_properties_call_set_finish() to get the result of the operation. + * + * See bluez_characteristic_org_freedesktop_dbus_properties_call_set_sync() for the synchronous, blocking version of this method. + */ +void +bluez_characteristic_org_freedesktop_dbus_properties_call_set ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant *arg_value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "Set", + g_variant_new ("(ss@v)", + arg_interface, + arg_name, + arg_value), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_call_set_finish: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusPropertiesProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic_org_freedesktop_dbus_properties_call_set(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_characteristic_org_freedesktop_dbus_properties_call_set(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic_org_freedesktop_dbus_properties_call_set_finish ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_call_set_sync: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @arg_name: Argument to pass with the method invocation. + * @arg_value: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the Set() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_characteristic_org_freedesktop_dbus_properties_call_set() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic_org_freedesktop_dbus_properties_call_set_sync ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant *arg_value, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "Set", + g_variant_new ("(ss@v)", + arg_interface, + arg_name, + arg_value), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_call_get_all: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the GetAll() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic_org_freedesktop_dbus_properties_call_get_all_finish() to get the result of the operation. + * + * See bluez_characteristic_org_freedesktop_dbus_properties_call_get_all_sync() for the synchronous, blocking version of this method. + */ +void +bluez_characteristic_org_freedesktop_dbus_properties_call_get_all ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "GetAll", + g_variant_new ("(s)", + arg_interface), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_call_get_all_finish: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusPropertiesProxy. + * @out_properties: (out): Return location for return parameter or %NULL to ignore. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic_org_freedesktop_dbus_properties_call_get_all(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_characteristic_org_freedesktop_dbus_properties_call_get_all(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic_org_freedesktop_dbus_properties_call_get_all_finish ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + GVariant **out_properties, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(@a{sv})", + out_properties); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_call_get_all_sync: + * @proxy: A #bluezcharacteristicOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @out_properties: (out): Return location for return parameter or %NULL to ignore. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the GetAll() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_characteristic_org_freedesktop_dbus_properties_call_get_all() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_characteristic_org_freedesktop_dbus_properties_call_get_all_sync ( + bluezcharacteristicOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + GVariant **out_properties, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "GetAll", + g_variant_new ("(s)", + arg_interface), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(@a{sv})", + out_properties); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_complete_get: + * @object: A #bluezcharacteristicOrgFreedesktopDBusProperties. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * @value: Parameter to return. + * + * Helper function used in service implementations to finish handling invocations of the Get() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_characteristic_org_freedesktop_dbus_properties_complete_get ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + GVariant *value) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(@v)", + value)); +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_complete_set: + * @object: A #bluezcharacteristicOrgFreedesktopDBusProperties. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the Set() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_characteristic_org_freedesktop_dbus_properties_complete_set ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_complete_get_all: + * @object: A #bluezcharacteristicOrgFreedesktopDBusProperties. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * @properties: Parameter to return. + * + * Helper function used in service implementations to finish handling invocations of the GetAll() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_characteristic_org_freedesktop_dbus_properties_complete_get_all ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + GVariant *properties) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(@a{sv})", + properties)); +} + +/* ------------------------------------------------------------------------ */ + +/** + * bluezcharacteristicOrgFreedesktopDBusPropertiesProxy: + * + * The #bluezcharacteristicOrgFreedesktopDBusPropertiesProxy structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezcharacteristicOrgFreedesktopDBusPropertiesProxyClass: + * @parent_class: The parent class. + * + * Class structure for #bluezcharacteristicOrgFreedesktopDBusPropertiesProxy. + */ + +struct _bluezcharacteristicOrgFreedesktopDBusPropertiesProxyPrivate +{ + GData *qdata; +}; + +static void bluez_characteristic_org_freedesktop_dbus_properties_proxy_iface_init (bluezcharacteristicOrgFreedesktopDBusPropertiesIface *iface); + +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicOrgFreedesktopDBusPropertiesProxy, bluez_characteristic_org_freedesktop_dbus_properties_proxy, G_TYPE_DBUS_PROXY, + G_ADD_PRIVATE (bluezcharacteristicOrgFreedesktopDBusPropertiesProxy) + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluez_characteristic_org_freedesktop_dbus_properties_proxy_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicOrgFreedesktopDBusPropertiesProxy, bluez_characteristic_org_freedesktop_dbus_properties_proxy, G_TYPE_DBUS_PROXY, + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluez_characteristic_org_freedesktop_dbus_properties_proxy_iface_init)); + +#endif +static void +bluez_characteristic_org_freedesktop_dbus_properties_proxy_finalize (GObject *object) +{ + bluezcharacteristicOrgFreedesktopDBusPropertiesProxy *proxy = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY (object); + g_datalist_clear (&proxy->priv->qdata); + G_OBJECT_CLASS (bluez_characteristic_org_freedesktop_dbus_properties_proxy_parent_class)->finalize (object); +} + +static void +bluez_characteristic_org_freedesktop_dbus_properties_proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ +} + +static void +bluez_characteristic_org_freedesktop_dbus_properties_proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ +} + +static void +bluez_characteristic_org_freedesktop_dbus_properties_proxy_g_signal (GDBusProxy *proxy, + const gchar *sender_name G_GNUC_UNUSED, + const gchar *signal_name, + GVariant *parameters) +{ + _ExtendedGDBusSignalInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint n; + guint signal_id; + info = (_ExtendedGDBusSignalInfo *) g_dbus_interface_info_lookup_signal ((GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_properties_interface_info.parent_struct, signal_name); + if (info == NULL) + return; + num_params = g_variant_n_children (parameters); + paramv = g_new0 (GValue, num_params + 1); + g_value_init (¶mv[0], BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES); + g_value_set_object (¶mv[0], proxy); + g_variant_iter_init (&iter, parameters); + n = 1; + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.args[n - 1]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES); + g_signal_emitv (paramv, signal_id, 0, NULL); + for (n = 0; n < num_params + 1; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static void +bluez_characteristic_org_freedesktop_dbus_properties_proxy_g_properties_changed (GDBusProxy *_proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties) +{ + bluezcharacteristicOrgFreedesktopDBusPropertiesProxy *proxy = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY (_proxy); + guint n; + const gchar *key; + GVariantIter *iter; + _ExtendedGDBusPropertyInfo *info; + g_variant_get (changed_properties, "a{sv}", &iter); + while (g_variant_iter_next (iter, "{&sv}", &key, NULL)) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_properties_interface_info.parent_struct, key); + g_datalist_remove_data (&proxy->priv->qdata, key); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } + g_variant_iter_free (iter); + for (n = 0; invalidated_properties[n] != NULL; n++) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_properties_interface_info.parent_struct, invalidated_properties[n]); + g_datalist_remove_data (&proxy->priv->qdata, invalidated_properties[n]); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } +} + +static void +bluez_characteristic_org_freedesktop_dbus_properties_proxy_init (bluezcharacteristicOrgFreedesktopDBusPropertiesProxy *proxy) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + proxy->priv = bluez_characteristic_org_freedesktop_dbus_properties_proxy_get_instance_private (proxy); +#else + proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, bluezcharacteristicOrgFreedesktopDBusPropertiesProxyPrivate); +#endif + + g_dbus_proxy_set_interface_info (G_DBUS_PROXY (proxy), bluez_characteristic_org_freedesktop_dbus_properties_interface_info ()); +} + +static void +bluez_characteristic_org_freedesktop_dbus_properties_proxy_class_init (bluezcharacteristicOrgFreedesktopDBusPropertiesProxyClass *klass) +{ + GObjectClass *gobject_class; + GDBusProxyClass *proxy_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_characteristic_org_freedesktop_dbus_properties_proxy_finalize; + gobject_class->get_property = bluez_characteristic_org_freedesktop_dbus_properties_proxy_get_property; + gobject_class->set_property = bluez_characteristic_org_freedesktop_dbus_properties_proxy_set_property; + + proxy_class = G_DBUS_PROXY_CLASS (klass); + proxy_class->g_signal = bluez_characteristic_org_freedesktop_dbus_properties_proxy_g_signal; + proxy_class->g_properties_changed = bluez_characteristic_org_freedesktop_dbus_properties_proxy_g_properties_changed; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezcharacteristicOrgFreedesktopDBusPropertiesProxyPrivate)); +#endif +} + +static void +bluez_characteristic_org_freedesktop_dbus_properties_proxy_iface_init (bluezcharacteristicOrgFreedesktopDBusPropertiesIface *iface) +{ +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_proxy_new: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Asynchronously creates a proxy for the D-Bus interface org.freedesktop.DBus.Properties. See g_dbus_proxy_new() for more details. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_finish() to get the result of the operation. + * + * See bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_characteristic_org_freedesktop_dbus_properties_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Properties", NULL); +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic_org_freedesktop_dbus_properties_proxy_new(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_characteristic_org_freedesktop_dbus_properties_proxy_new(). + * + * Returns: (transfer full) (type bluezcharacteristicOrgFreedesktopDBusPropertiesProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristicOrgFreedesktopDBusProperties * +bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES (ret); + else + return NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_sync: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Synchronously creates a proxy for the D-Bus interface org.freedesktop.DBus.Properties. See g_dbus_proxy_new_sync() for more details. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_characteristic_org_freedesktop_dbus_properties_proxy_new() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezcharacteristicOrgFreedesktopDBusPropertiesProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristicOrgFreedesktopDBusProperties * +bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Properties", NULL); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES (ret); + else + return NULL; +} + + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Like bluez_characteristic_org_freedesktop_dbus_properties_proxy_new() but takes a #GBusType instead of a #GDBusConnection. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus_finish() to get the result of the operation. + * + * See bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Properties", NULL); +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus(). + * + * Returns: (transfer full) (type bluezcharacteristicOrgFreedesktopDBusPropertiesProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristicOrgFreedesktopDBusProperties * +bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES (ret); + else + return NULL; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus_sync: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Like bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_sync() but takes a #GBusType instead of a #GDBusConnection. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezcharacteristicOrgFreedesktopDBusPropertiesProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezcharacteristicOrgFreedesktopDBusProperties * +bluez_characteristic_org_freedesktop_dbus_properties_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Properties", NULL); + if (ret != NULL) + return BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES (ret); + else + return NULL; +} + + +/* ------------------------------------------------------------------------ */ + +/** + * bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton: + * + * The #bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonClass: + * @parent_class: The parent class. + * + * Class structure for #bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton. + */ + +struct _bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonPrivate +{ + GValue *properties; + GList *changed_properties; + GSource *changed_properties_idle_source; + GMainContext *context; + GMutex lock; +}; + +static void +_bluez_characteristic_org_freedesktop_dbus_properties_skeleton_handle_method_call ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (user_data); + _ExtendedGDBusMethodInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint num_extra; + guint n; + guint signal_id; + GValue return_value = G_VALUE_INIT; + info = (_ExtendedGDBusMethodInfo *) g_dbus_method_invocation_get_method_info (invocation); + g_assert (info != NULL); + num_params = g_variant_n_children (parameters); + num_extra = info->pass_fdlist ? 3 : 2; paramv = g_new0 (GValue, num_params + num_extra); + n = 0; + g_value_init (¶mv[n], BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES); + g_value_set_object (¶mv[n++], skeleton); + g_value_init (¶mv[n], G_TYPE_DBUS_METHOD_INVOCATION); + g_value_set_object (¶mv[n++], invocation); + if (info->pass_fdlist) + { +#ifdef G_OS_UNIX + g_value_init (¶mv[n], G_TYPE_UNIX_FD_LIST); + g_value_set_object (¶mv[n++], g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation))); +#else + g_assert_not_reached (); +#endif + } + g_variant_iter_init (&iter, parameters); + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.in_args[n - num_extra]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES); + g_value_init (&return_value, G_TYPE_BOOLEAN); + g_signal_emitv (paramv, signal_id, 0, &return_value); + if (!g_value_get_boolean (&return_value)) + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method %s is not implemented on interface %s", method_name, interface_name); + g_value_unset (&return_value); + for (n = 0; n < num_params + num_extra; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static GVariant * +_bluez_characteristic_org_freedesktop_dbus_properties_skeleton_handle_get_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + GVariant *ret; + ret = NULL; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_properties_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + g_value_init (&value, pspec->value_type); + g_object_get_property (G_OBJECT (skeleton), info->hyphen_name, &value); + ret = g_dbus_gvalue_to_gvariant (&value, G_VARIANT_TYPE (info->parent_struct.signature)); + g_value_unset (&value); + } + return ret; +} + +static gboolean +_bluez_characteristic_org_freedesktop_dbus_properties_skeleton_handle_set_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GVariant *variant, + GError **error, + gpointer user_data) +{ + bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + gboolean ret; + ret = FALSE; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_characteristic_org_freedesktop_dbus_properties_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + if (info->use_gvariant) + g_value_set_variant (&value, variant); + else + g_dbus_gvariant_to_gvalue (variant, &value); + g_object_set_property (G_OBJECT (skeleton), info->hyphen_name, &value); + g_value_unset (&value); + ret = TRUE; + } + return ret; +} + +static const GDBusInterfaceVTable _bluez_characteristic_org_freedesktop_dbus_properties_skeleton_vtable = +{ + _bluez_characteristic_org_freedesktop_dbus_properties_skeleton_handle_method_call, + _bluez_characteristic_org_freedesktop_dbus_properties_skeleton_handle_get_property, + _bluez_characteristic_org_freedesktop_dbus_properties_skeleton_handle_set_property, + {NULL} +}; + +static GDBusInterfaceInfo * +bluez_characteristic_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_info (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return bluez_characteristic_org_freedesktop_dbus_properties_interface_info (); +} + +static GDBusInterfaceVTable * +bluez_characteristic_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_vtable (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return (GDBusInterfaceVTable *) &_bluez_characteristic_org_freedesktop_dbus_properties_skeleton_vtable; +} + +static GVariant * +bluez_characteristic_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_properties (GDBusInterfaceSkeleton *_skeleton) +{ + bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (_skeleton); + + GVariantBuilder builder; + guint n; + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + if (_bluez_characteristic_org_freedesktop_dbus_properties_interface_info.parent_struct.properties == NULL) + goto out; + for (n = 0; _bluez_characteristic_org_freedesktop_dbus_properties_interface_info.parent_struct.properties[n] != NULL; n++) + { + GDBusPropertyInfo *info = _bluez_characteristic_org_freedesktop_dbus_properties_interface_info.parent_struct.properties[n]; + if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) + { + GVariant *value; + value = _bluez_characteristic_org_freedesktop_dbus_properties_skeleton_handle_get_property (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)), NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.freedesktop.DBus.Properties", info->name, NULL, skeleton); + if (value != NULL) + { + g_variant_take_ref (value); + g_variant_builder_add (&builder, "{sv}", info->name, value); + g_variant_unref (value); + } + } + } +out: + return g_variant_builder_end (&builder); +} + +static void +bluez_characteristic_org_freedesktop_dbus_properties_skeleton_dbus_interface_flush (GDBusInterfaceSkeleton *_skeleton) +{ +} + +static void +_bluez_characteristic_org_freedesktop_dbus_properties_on_signal_properties_changed ( + bluezcharacteristicOrgFreedesktopDBusProperties *object, + const gchar *arg_interface, + GVariant *arg_changed_properties, + const gchar *const *arg_invalidated_properties) +{ + bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (object); + + GList *connections, *l; + GVariant *signal_variant; + connections = g_dbus_interface_skeleton_get_connections (G_DBUS_INTERFACE_SKELETON (skeleton)); + + signal_variant = g_variant_ref_sink (g_variant_new ("(s@a{sv}^as)", + arg_interface, + arg_changed_properties, + arg_invalidated_properties)); + for (l = connections; l != NULL; l = l->next) + { + GDBusConnection *connection = l->data; + g_dbus_connection_emit_signal (connection, + NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.freedesktop.DBus.Properties", "PropertiesChanged", + signal_variant, NULL); + } + g_variant_unref (signal_variant); + g_list_free_full (connections, g_object_unref); +} + +static void bluez_characteristic_org_freedesktop_dbus_properties_skeleton_iface_init (bluezcharacteristicOrgFreedesktopDBusPropertiesIface *iface); +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton, bluez_characteristic_org_freedesktop_dbus_properties_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_ADD_PRIVATE (bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton) + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluez_characteristic_org_freedesktop_dbus_properties_skeleton_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton, bluez_characteristic_org_freedesktop_dbus_properties_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_IMPLEMENT_INTERFACE (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluez_characteristic_org_freedesktop_dbus_properties_skeleton_iface_init)); + +#endif +static void +bluez_characteristic_org_freedesktop_dbus_properties_skeleton_finalize (GObject *object) +{ + bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (object); + g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free); + if (skeleton->priv->changed_properties_idle_source != NULL) + g_source_destroy (skeleton->priv->changed_properties_idle_source); + g_main_context_unref (skeleton->priv->context); + g_mutex_clear (&skeleton->priv->lock); + G_OBJECT_CLASS (bluez_characteristic_org_freedesktop_dbus_properties_skeleton_parent_class)->finalize (object); +} + +static void +bluez_characteristic_org_freedesktop_dbus_properties_skeleton_init (bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton *skeleton) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + skeleton->priv = bluez_characteristic_org_freedesktop_dbus_properties_skeleton_get_instance_private (skeleton); +#else + skeleton->priv = G_TYPE_INSTANCE_GET_PRIVATE (skeleton, BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON, bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonPrivate); +#endif + + g_mutex_init (&skeleton->priv->lock); + skeleton->priv->context = g_main_context_ref_thread_default (); +} + +static void +bluez_characteristic_org_freedesktop_dbus_properties_skeleton_class_init (bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonClass *klass) +{ + GObjectClass *gobject_class; + GDBusInterfaceSkeletonClass *skeleton_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_characteristic_org_freedesktop_dbus_properties_skeleton_finalize; + + skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass); + skeleton_class->get_info = bluez_characteristic_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_info; + skeleton_class->get_properties = bluez_characteristic_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_properties; + skeleton_class->flush = bluez_characteristic_org_freedesktop_dbus_properties_skeleton_dbus_interface_flush; + skeleton_class->get_vtable = bluez_characteristic_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_vtable; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezcharacteristicOrgFreedesktopDBusPropertiesSkeletonPrivate)); +#endif +} + +static void +bluez_characteristic_org_freedesktop_dbus_properties_skeleton_iface_init (bluezcharacteristicOrgFreedesktopDBusPropertiesIface *iface) +{ + iface->properties_changed = _bluez_characteristic_org_freedesktop_dbus_properties_on_signal_properties_changed; +} + +/** + * bluez_characteristic_org_freedesktop_dbus_properties_skeleton_new: + * + * Creates a skeleton object for the D-Bus interface org.freedesktop.DBus.Properties. + * + * Returns: (transfer full) (type bluezcharacteristicOrgFreedesktopDBusPropertiesSkeleton): The skeleton object. + */ +bluezcharacteristicOrgFreedesktopDBusProperties * +bluez_characteristic_org_freedesktop_dbus_properties_skeleton_new (void) +{ + return BLUEZ_CHARACTERISTIC_ORG_FREEDESKTOP_DBUS_PROPERTIES (g_object_new (BLUEZ_CHARACTERISTIC_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON, NULL)); +} + diff --git a/modules/ble/deps/linux/dbus-bluez/src/bluez_device.c b/modules/ble/deps/linux/dbus-bluez/src/bluez_device.c new file mode 100644 index 00000000..a225bfa3 --- /dev/null +++ b/modules/ble/deps/linux/dbus-bluez/src/bluez_device.c @@ -0,0 +1,7335 @@ +/* + * Generated by gdbus-codegen 2.40.0. DO NOT EDIT. + * + * The license of this code is the same as for the source it was derived from. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "bluez_device.h" + +#include +#ifdef G_OS_UNIX +# include +#endif + +typedef struct +{ + GDBusArgInfo parent_struct; + gboolean use_gvariant; +} _ExtendedGDBusArgInfo; + +typedef struct +{ + GDBusMethodInfo parent_struct; + const gchar *signal_name; + gboolean pass_fdlist; +} _ExtendedGDBusMethodInfo; + +typedef struct +{ + GDBusSignalInfo parent_struct; + const gchar *signal_name; +} _ExtendedGDBusSignalInfo; + +typedef struct +{ + GDBusPropertyInfo parent_struct; + const gchar *hyphen_name; + gboolean use_gvariant; +} _ExtendedGDBusPropertyInfo; + +typedef struct +{ + GDBusInterfaceInfo parent_struct; + const gchar *hyphen_name; +} _ExtendedGDBusInterfaceInfo; + +typedef struct +{ + const _ExtendedGDBusPropertyInfo *info; + guint prop_id; + GValue orig_value; /* the value before the change */ +} ChangedProperty; + +static void +_changed_property_free (ChangedProperty *data) +{ + g_value_unset (&data->orig_value); + g_free (data); +} + +static gboolean +_g_strv_equal0 (gchar **a, gchar **b) +{ + gboolean ret = FALSE; + guint n; + if (a == NULL && b == NULL) + { + ret = TRUE; + goto out; + } + if (a == NULL || b == NULL) + goto out; + if (g_strv_length (a) != g_strv_length (b)) + goto out; + for (n = 0; a[n] != NULL; n++) + if (g_strcmp0 (a[n], b[n]) != 0) + goto out; + ret = TRUE; +out: + return ret; +} + +static gboolean +_g_variant_equal0 (GVariant *a, GVariant *b) +{ + gboolean ret = FALSE; + if (a == NULL && b == NULL) + { + ret = TRUE; + goto out; + } + if (a == NULL || b == NULL) + goto out; + ret = g_variant_equal (a, b); +out: + return ret; +} + +G_GNUC_UNUSED static gboolean +_g_value_equal (const GValue *a, const GValue *b) +{ + gboolean ret = FALSE; + g_assert (G_VALUE_TYPE (a) == G_VALUE_TYPE (b)); + switch (G_VALUE_TYPE (a)) + { + case G_TYPE_BOOLEAN: + ret = (g_value_get_boolean (a) == g_value_get_boolean (b)); + break; + case G_TYPE_UCHAR: + ret = (g_value_get_uchar (a) == g_value_get_uchar (b)); + break; + case G_TYPE_INT: + ret = (g_value_get_int (a) == g_value_get_int (b)); + break; + case G_TYPE_UINT: + ret = (g_value_get_uint (a) == g_value_get_uint (b)); + break; + case G_TYPE_INT64: + ret = (g_value_get_int64 (a) == g_value_get_int64 (b)); + break; + case G_TYPE_UINT64: + ret = (g_value_get_uint64 (a) == g_value_get_uint64 (b)); + break; + case G_TYPE_DOUBLE: + { + /* Avoid -Wfloat-equal warnings by doing a direct bit compare */ + gdouble da = g_value_get_double (a); + gdouble db = g_value_get_double (b); + ret = memcmp (&da, &db, sizeof (gdouble)) == 0; + } + break; + case G_TYPE_STRING: + ret = (g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0); + break; + case G_TYPE_VARIANT: + ret = _g_variant_equal0 (g_value_get_variant (a), g_value_get_variant (b)); + break; + default: + if (G_VALUE_TYPE (a) == G_TYPE_STRV) + ret = _g_strv_equal0 (g_value_get_boxed (a), g_value_get_boxed (b)); + else + g_critical ("_g_value_equal() does not handle type %s", g_type_name (G_VALUE_TYPE (a))); + break; + } + return ret; +} + +/* ------------------------------------------------------------------------ + * Code for interface org.freedesktop.DBus.Introspectable + * ------------------------------------------------------------------------ + */ + +/** + * SECTION:bluezdeviceOrgFreedesktopDBusIntrospectable + * @title: bluezdeviceOrgFreedesktopDBusIntrospectable + * @short_description: Generated C code for the org.freedesktop.DBus.Introspectable D-Bus interface + * + * This section contains code for working with the org.freedesktop.DBus.Introspectable D-Bus interface in C. + */ + +/* ---- Introspection data for org.freedesktop.DBus.Introspectable ---- */ + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_introspectable_method_info_introspect_OUT_ARG_xml = +{ + { + -1, + (gchar *) "xml", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_device_org_freedesktop_dbus_introspectable_method_info_introspect_OUT_ARG_pointers[] = +{ + &_bluez_device_org_freedesktop_dbus_introspectable_method_info_introspect_OUT_ARG_xml, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_device_org_freedesktop_dbus_introspectable_method_info_introspect = +{ + { + -1, + (gchar *) "Introspect", + NULL, + (GDBusArgInfo **) &_bluez_device_org_freedesktop_dbus_introspectable_method_info_introspect_OUT_ARG_pointers, + NULL + }, + "handle-introspect", + FALSE +}; + +static const _ExtendedGDBusMethodInfo * const _bluez_device_org_freedesktop_dbus_introspectable_method_info_pointers[] = +{ + &_bluez_device_org_freedesktop_dbus_introspectable_method_info_introspect, + NULL +}; + +static const _ExtendedGDBusInterfaceInfo _bluez_device_org_freedesktop_dbus_introspectable_interface_info = +{ + { + -1, + (gchar *) "org.freedesktop.DBus.Introspectable", + (GDBusMethodInfo **) &_bluez_device_org_freedesktop_dbus_introspectable_method_info_pointers, + NULL, + NULL, + NULL + }, + "org-freedesktop-dbus-introspectable", +}; + + +/** + * bluez_device_org_freedesktop_dbus_introspectable_interface_info: + * + * Gets a machine-readable description of the org.freedesktop.DBus.Introspectable D-Bus interface. + * + * Returns: (transfer none): A #GDBusInterfaceInfo. Do not free. + */ +GDBusInterfaceInfo * +bluez_device_org_freedesktop_dbus_introspectable_interface_info (void) +{ + return (GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_introspectable_interface_info.parent_struct; +} + +/** + * bluez_device_org_freedesktop_dbus_introspectable_override_properties: + * @klass: The class structure for a #GObject-derived class. + * @property_id_begin: The property id to assign to the first overridden property. + * + * Overrides all #GObject properties in the #bluezdeviceOrgFreedesktopDBusIntrospectable interface for a concrete class. + * The properties are overridden in the order they are defined. + * + * Returns: The last property id. + */ +guint +bluez_device_org_freedesktop_dbus_introspectable_override_properties (GObjectClass *klass, guint property_id_begin) +{ + return property_id_begin - 1; +} + + + +/** + * bluezdeviceOrgFreedesktopDBusIntrospectable: + * + * Abstract interface type for the D-Bus interface org.freedesktop.DBus.Introspectable. + */ + +/** + * bluezdeviceOrgFreedesktopDBusIntrospectableIface: + * @parent_iface: The parent interface. + * @handle_introspect: Handler for the #bluezdeviceOrgFreedesktopDBusIntrospectable::handle-introspect signal. + * + * Virtual table for the D-Bus interface org.freedesktop.DBus.Introspectable. + */ + +typedef bluezdeviceOrgFreedesktopDBusIntrospectableIface bluezdeviceOrgFreedesktopDBusIntrospectableInterface; +G_DEFINE_INTERFACE (bluezdeviceOrgFreedesktopDBusIntrospectable, bluez_device_org_freedesktop_dbus_introspectable, G_TYPE_OBJECT); + +static void +bluez_device_org_freedesktop_dbus_introspectable_default_init (bluezdeviceOrgFreedesktopDBusIntrospectableIface *iface) +{ + /* GObject signals for incoming D-Bus method calls: */ + /** + * bluezdeviceOrgFreedesktopDBusIntrospectable::handle-introspect: + * @object: A #bluezdeviceOrgFreedesktopDBusIntrospectable. + * @invocation: A #GDBusMethodInvocation. + * + * Signal emitted when a remote caller is invoking the Introspect() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_device_org_freedesktop_dbus_introspectable_complete_introspect() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-introspect", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezdeviceOrgFreedesktopDBusIntrospectableIface, handle_introspect), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 1, + G_TYPE_DBUS_METHOD_INVOCATION); + +} + +/** + * bluez_device_org_freedesktop_dbus_introspectable_call_introspect: + * @proxy: A #bluezdeviceOrgFreedesktopDBusIntrospectableProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the Introspect() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device_org_freedesktop_dbus_introspectable_call_introspect_finish() to get the result of the operation. + * + * See bluez_device_org_freedesktop_dbus_introspectable_call_introspect_sync() for the synchronous, blocking version of this method. + */ +void +bluez_device_org_freedesktop_dbus_introspectable_call_introspect ( + bluezdeviceOrgFreedesktopDBusIntrospectable *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "Introspect", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_device_org_freedesktop_dbus_introspectable_call_introspect_finish: + * @proxy: A #bluezdeviceOrgFreedesktopDBusIntrospectableProxy. + * @out_xml: (out): Return location for return parameter or %NULL to ignore. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device_org_freedesktop_dbus_introspectable_call_introspect(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_device_org_freedesktop_dbus_introspectable_call_introspect(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device_org_freedesktop_dbus_introspectable_call_introspect_finish ( + bluezdeviceOrgFreedesktopDBusIntrospectable *proxy, + gchar **out_xml, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(s)", + out_xml); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_introspectable_call_introspect_sync: + * @proxy: A #bluezdeviceOrgFreedesktopDBusIntrospectableProxy. + * @out_xml: (out): Return location for return parameter or %NULL to ignore. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the Introspect() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_device_org_freedesktop_dbus_introspectable_call_introspect() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device_org_freedesktop_dbus_introspectable_call_introspect_sync ( + bluezdeviceOrgFreedesktopDBusIntrospectable *proxy, + gchar **out_xml, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "Introspect", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(s)", + out_xml); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_introspectable_complete_introspect: + * @object: A #bluezdeviceOrgFreedesktopDBusIntrospectable. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * @xml: Parameter to return. + * + * Helper function used in service implementations to finish handling invocations of the Introspect() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_device_org_freedesktop_dbus_introspectable_complete_introspect ( + bluezdeviceOrgFreedesktopDBusIntrospectable *object, + GDBusMethodInvocation *invocation, + const gchar *xml) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", + xml)); +} + +/* ------------------------------------------------------------------------ */ + +/** + * bluezdeviceOrgFreedesktopDBusIntrospectableProxy: + * + * The #bluezdeviceOrgFreedesktopDBusIntrospectableProxy structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezdeviceOrgFreedesktopDBusIntrospectableProxyClass: + * @parent_class: The parent class. + * + * Class structure for #bluezdeviceOrgFreedesktopDBusIntrospectableProxy. + */ + +struct _bluezdeviceOrgFreedesktopDBusIntrospectableProxyPrivate +{ + GData *qdata; +}; + +static void bluez_device_org_freedesktop_dbus_introspectable_proxy_iface_init (bluezdeviceOrgFreedesktopDBusIntrospectableIface *iface); + +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgFreedesktopDBusIntrospectableProxy, bluez_device_org_freedesktop_dbus_introspectable_proxy, G_TYPE_DBUS_PROXY, + G_ADD_PRIVATE (bluezdeviceOrgFreedesktopDBusIntrospectableProxy) + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluez_device_org_freedesktop_dbus_introspectable_proxy_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgFreedesktopDBusIntrospectableProxy, bluez_device_org_freedesktop_dbus_introspectable_proxy, G_TYPE_DBUS_PROXY, + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluez_device_org_freedesktop_dbus_introspectable_proxy_iface_init)); + +#endif +static void +bluez_device_org_freedesktop_dbus_introspectable_proxy_finalize (GObject *object) +{ + bluezdeviceOrgFreedesktopDBusIntrospectableProxy *proxy = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY (object); + g_datalist_clear (&proxy->priv->qdata); + G_OBJECT_CLASS (bluez_device_org_freedesktop_dbus_introspectable_proxy_parent_class)->finalize (object); +} + +static void +bluez_device_org_freedesktop_dbus_introspectable_proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ +} + +static void +bluez_device_org_freedesktop_dbus_introspectable_proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ +} + +static void +bluez_device_org_freedesktop_dbus_introspectable_proxy_g_signal (GDBusProxy *proxy, + const gchar *sender_name G_GNUC_UNUSED, + const gchar *signal_name, + GVariant *parameters) +{ + _ExtendedGDBusSignalInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint n; + guint signal_id; + info = (_ExtendedGDBusSignalInfo *) g_dbus_interface_info_lookup_signal ((GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_introspectable_interface_info.parent_struct, signal_name); + if (info == NULL) + return; + num_params = g_variant_n_children (parameters); + paramv = g_new0 (GValue, num_params + 1); + g_value_init (¶mv[0], BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE); + g_value_set_object (¶mv[0], proxy); + g_variant_iter_init (&iter, parameters); + n = 1; + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.args[n - 1]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE); + g_signal_emitv (paramv, signal_id, 0, NULL); + for (n = 0; n < num_params + 1; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static void +bluez_device_org_freedesktop_dbus_introspectable_proxy_g_properties_changed (GDBusProxy *_proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties) +{ + bluezdeviceOrgFreedesktopDBusIntrospectableProxy *proxy = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY (_proxy); + guint n; + const gchar *key; + GVariantIter *iter; + _ExtendedGDBusPropertyInfo *info; + g_variant_get (changed_properties, "a{sv}", &iter); + while (g_variant_iter_next (iter, "{&sv}", &key, NULL)) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_introspectable_interface_info.parent_struct, key); + g_datalist_remove_data (&proxy->priv->qdata, key); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } + g_variant_iter_free (iter); + for (n = 0; invalidated_properties[n] != NULL; n++) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_introspectable_interface_info.parent_struct, invalidated_properties[n]); + g_datalist_remove_data (&proxy->priv->qdata, invalidated_properties[n]); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } +} + +static void +bluez_device_org_freedesktop_dbus_introspectable_proxy_init (bluezdeviceOrgFreedesktopDBusIntrospectableProxy *proxy) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + proxy->priv = bluez_device_org_freedesktop_dbus_introspectable_proxy_get_instance_private (proxy); +#else + proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, bluezdeviceOrgFreedesktopDBusIntrospectableProxyPrivate); +#endif + + g_dbus_proxy_set_interface_info (G_DBUS_PROXY (proxy), bluez_device_org_freedesktop_dbus_introspectable_interface_info ()); +} + +static void +bluez_device_org_freedesktop_dbus_introspectable_proxy_class_init (bluezdeviceOrgFreedesktopDBusIntrospectableProxyClass *klass) +{ + GObjectClass *gobject_class; + GDBusProxyClass *proxy_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_device_org_freedesktop_dbus_introspectable_proxy_finalize; + gobject_class->get_property = bluez_device_org_freedesktop_dbus_introspectable_proxy_get_property; + gobject_class->set_property = bluez_device_org_freedesktop_dbus_introspectable_proxy_set_property; + + proxy_class = G_DBUS_PROXY_CLASS (klass); + proxy_class->g_signal = bluez_device_org_freedesktop_dbus_introspectable_proxy_g_signal; + proxy_class->g_properties_changed = bluez_device_org_freedesktop_dbus_introspectable_proxy_g_properties_changed; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezdeviceOrgFreedesktopDBusIntrospectableProxyPrivate)); +#endif +} + +static void +bluez_device_org_freedesktop_dbus_introspectable_proxy_iface_init (bluezdeviceOrgFreedesktopDBusIntrospectableIface *iface) +{ +} + +/** + * bluez_device_org_freedesktop_dbus_introspectable_proxy_new: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Asynchronously creates a proxy for the D-Bus interface org.freedesktop.DBus.Introspectable. See g_dbus_proxy_new() for more details. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device_org_freedesktop_dbus_introspectable_proxy_new_finish() to get the result of the operation. + * + * See bluez_device_org_freedesktop_dbus_introspectable_proxy_new_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_device_org_freedesktop_dbus_introspectable_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Introspectable", NULL); +} + +/** + * bluez_device_org_freedesktop_dbus_introspectable_proxy_new_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device_org_freedesktop_dbus_introspectable_proxy_new(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_device_org_freedesktop_dbus_introspectable_proxy_new(). + * + * Returns: (transfer full) (type bluezdeviceOrgFreedesktopDBusIntrospectableProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgFreedesktopDBusIntrospectable * +bluez_device_org_freedesktop_dbus_introspectable_proxy_new_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (ret); + else + return NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_introspectable_proxy_new_sync: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Synchronously creates a proxy for the D-Bus interface org.freedesktop.DBus.Introspectable. See g_dbus_proxy_new_sync() for more details. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_device_org_freedesktop_dbus_introspectable_proxy_new() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezdeviceOrgFreedesktopDBusIntrospectableProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgFreedesktopDBusIntrospectable * +bluez_device_org_freedesktop_dbus_introspectable_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Introspectable", NULL); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (ret); + else + return NULL; +} + + +/** + * bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Like bluez_device_org_freedesktop_dbus_introspectable_proxy_new() but takes a #GBusType instead of a #GDBusConnection. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus_finish() to get the result of the operation. + * + * See bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Introspectable", NULL); +} + +/** + * bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus(). + * + * Returns: (transfer full) (type bluezdeviceOrgFreedesktopDBusIntrospectableProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgFreedesktopDBusIntrospectable * +bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (ret); + else + return NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus_sync: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Like bluez_device_org_freedesktop_dbus_introspectable_proxy_new_sync() but takes a #GBusType instead of a #GDBusConnection. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezdeviceOrgFreedesktopDBusIntrospectableProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgFreedesktopDBusIntrospectable * +bluez_device_org_freedesktop_dbus_introspectable_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Introspectable", NULL); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (ret); + else + return NULL; +} + + +/* ------------------------------------------------------------------------ */ + +/** + * bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton: + * + * The #bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonClass: + * @parent_class: The parent class. + * + * Class structure for #bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton. + */ + +struct _bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonPrivate +{ + GValue *properties; + GList *changed_properties; + GSource *changed_properties_idle_source; + GMainContext *context; + GMutex lock; +}; + +static void +_bluez_device_org_freedesktop_dbus_introspectable_skeleton_handle_method_call ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton *skeleton = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (user_data); + _ExtendedGDBusMethodInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint num_extra; + guint n; + guint signal_id; + GValue return_value = G_VALUE_INIT; + info = (_ExtendedGDBusMethodInfo *) g_dbus_method_invocation_get_method_info (invocation); + g_assert (info != NULL); + num_params = g_variant_n_children (parameters); + num_extra = info->pass_fdlist ? 3 : 2; paramv = g_new0 (GValue, num_params + num_extra); + n = 0; + g_value_init (¶mv[n], BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE); + g_value_set_object (¶mv[n++], skeleton); + g_value_init (¶mv[n], G_TYPE_DBUS_METHOD_INVOCATION); + g_value_set_object (¶mv[n++], invocation); + if (info->pass_fdlist) + { +#ifdef G_OS_UNIX + g_value_init (¶mv[n], G_TYPE_UNIX_FD_LIST); + g_value_set_object (¶mv[n++], g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation))); +#else + g_assert_not_reached (); +#endif + } + g_variant_iter_init (&iter, parameters); + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.in_args[n - num_extra]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE); + g_value_init (&return_value, G_TYPE_BOOLEAN); + g_signal_emitv (paramv, signal_id, 0, &return_value); + if (!g_value_get_boolean (&return_value)) + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method %s is not implemented on interface %s", method_name, interface_name); + g_value_unset (&return_value); + for (n = 0; n < num_params + num_extra; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static GVariant * +_bluez_device_org_freedesktop_dbus_introspectable_skeleton_handle_get_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton *skeleton = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + GVariant *ret; + ret = NULL; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_introspectable_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + g_value_init (&value, pspec->value_type); + g_object_get_property (G_OBJECT (skeleton), info->hyphen_name, &value); + ret = g_dbus_gvalue_to_gvariant (&value, G_VARIANT_TYPE (info->parent_struct.signature)); + g_value_unset (&value); + } + return ret; +} + +static gboolean +_bluez_device_org_freedesktop_dbus_introspectable_skeleton_handle_set_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GVariant *variant, + GError **error, + gpointer user_data) +{ + bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton *skeleton = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + gboolean ret; + ret = FALSE; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_introspectable_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + if (info->use_gvariant) + g_value_set_variant (&value, variant); + else + g_dbus_gvariant_to_gvalue (variant, &value); + g_object_set_property (G_OBJECT (skeleton), info->hyphen_name, &value); + g_value_unset (&value); + ret = TRUE; + } + return ret; +} + +static const GDBusInterfaceVTable _bluez_device_org_freedesktop_dbus_introspectable_skeleton_vtable = +{ + _bluez_device_org_freedesktop_dbus_introspectable_skeleton_handle_method_call, + _bluez_device_org_freedesktop_dbus_introspectable_skeleton_handle_get_property, + _bluez_device_org_freedesktop_dbus_introspectable_skeleton_handle_set_property, + {NULL} +}; + +static GDBusInterfaceInfo * +bluez_device_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_info (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return bluez_device_org_freedesktop_dbus_introspectable_interface_info (); +} + +static GDBusInterfaceVTable * +bluez_device_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_vtable (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return (GDBusInterfaceVTable *) &_bluez_device_org_freedesktop_dbus_introspectable_skeleton_vtable; +} + +static GVariant * +bluez_device_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_properties (GDBusInterfaceSkeleton *_skeleton) +{ + bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton *skeleton = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (_skeleton); + + GVariantBuilder builder; + guint n; + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + if (_bluez_device_org_freedesktop_dbus_introspectable_interface_info.parent_struct.properties == NULL) + goto out; + for (n = 0; _bluez_device_org_freedesktop_dbus_introspectable_interface_info.parent_struct.properties[n] != NULL; n++) + { + GDBusPropertyInfo *info = _bluez_device_org_freedesktop_dbus_introspectable_interface_info.parent_struct.properties[n]; + if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) + { + GVariant *value; + value = _bluez_device_org_freedesktop_dbus_introspectable_skeleton_handle_get_property (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)), NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.freedesktop.DBus.Introspectable", info->name, NULL, skeleton); + if (value != NULL) + { + g_variant_take_ref (value); + g_variant_builder_add (&builder, "{sv}", info->name, value); + g_variant_unref (value); + } + } + } +out: + return g_variant_builder_end (&builder); +} + +static void +bluez_device_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_flush (GDBusInterfaceSkeleton *_skeleton) +{ +} + +static void bluez_device_org_freedesktop_dbus_introspectable_skeleton_iface_init (bluezdeviceOrgFreedesktopDBusIntrospectableIface *iface); +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton, bluez_device_org_freedesktop_dbus_introspectable_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_ADD_PRIVATE (bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton) + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluez_device_org_freedesktop_dbus_introspectable_skeleton_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton, bluez_device_org_freedesktop_dbus_introspectable_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE, bluez_device_org_freedesktop_dbus_introspectable_skeleton_iface_init)); + +#endif +static void +bluez_device_org_freedesktop_dbus_introspectable_skeleton_finalize (GObject *object) +{ + bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton *skeleton = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON (object); + g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free); + if (skeleton->priv->changed_properties_idle_source != NULL) + g_source_destroy (skeleton->priv->changed_properties_idle_source); + g_main_context_unref (skeleton->priv->context); + g_mutex_clear (&skeleton->priv->lock); + G_OBJECT_CLASS (bluez_device_org_freedesktop_dbus_introspectable_skeleton_parent_class)->finalize (object); +} + +static void +bluez_device_org_freedesktop_dbus_introspectable_skeleton_init (bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton *skeleton) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + skeleton->priv = bluez_device_org_freedesktop_dbus_introspectable_skeleton_get_instance_private (skeleton); +#else + skeleton->priv = G_TYPE_INSTANCE_GET_PRIVATE (skeleton, BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON, bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonPrivate); +#endif + + g_mutex_init (&skeleton->priv->lock); + skeleton->priv->context = g_main_context_ref_thread_default (); +} + +static void +bluez_device_org_freedesktop_dbus_introspectable_skeleton_class_init (bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonClass *klass) +{ + GObjectClass *gobject_class; + GDBusInterfaceSkeletonClass *skeleton_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_device_org_freedesktop_dbus_introspectable_skeleton_finalize; + + skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass); + skeleton_class->get_info = bluez_device_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_info; + skeleton_class->get_properties = bluez_device_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_properties; + skeleton_class->flush = bluez_device_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_flush; + skeleton_class->get_vtable = bluez_device_org_freedesktop_dbus_introspectable_skeleton_dbus_interface_get_vtable; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezdeviceOrgFreedesktopDBusIntrospectableSkeletonPrivate)); +#endif +} + +static void +bluez_device_org_freedesktop_dbus_introspectable_skeleton_iface_init (bluezdeviceOrgFreedesktopDBusIntrospectableIface *iface) +{ +} + +/** + * bluez_device_org_freedesktop_dbus_introspectable_skeleton_new: + * + * Creates a skeleton object for the D-Bus interface org.freedesktop.DBus.Introspectable. + * + * Returns: (transfer full) (type bluezdeviceOrgFreedesktopDBusIntrospectableSkeleton): The skeleton object. + */ +bluezdeviceOrgFreedesktopDBusIntrospectable * +bluez_device_org_freedesktop_dbus_introspectable_skeleton_new (void) +{ + return BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE (g_object_new (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_INTROSPECTABLE_SKELETON, NULL)); +} + +/* ------------------------------------------------------------------------ + * Code for interface org.bluez.Device1 + * ------------------------------------------------------------------------ + */ + +/** + * SECTION:bluezdevice + * @title: bluezdevice + * @short_description: Generated C code for the org.bluez.Device1 D-Bus interface + * + * This section contains code for working with the org.bluez.Device1 D-Bus interface in C. + */ + +/* ---- Introspection data for org.bluez.Device1 ---- */ + +static const _ExtendedGDBusMethodInfo _bluez_device__method_info_disconnect = +{ + { + -1, + (gchar *) "Disconnect", + NULL, + NULL, + NULL + }, + "handle-disconnect", + FALSE +}; + +static const _ExtendedGDBusMethodInfo _bluez_device__method_info_connect = +{ + { + -1, + (gchar *) "Connect", + NULL, + NULL, + NULL + }, + "handle-connect", + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_device__method_info_connect_profile_IN_ARG_UUID = +{ + { + -1, + (gchar *) "UUID", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_device__method_info_connect_profile_IN_ARG_pointers[] = +{ + &_bluez_device__method_info_connect_profile_IN_ARG_UUID, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_device__method_info_connect_profile = +{ + { + -1, + (gchar *) "ConnectProfile", + (GDBusArgInfo **) &_bluez_device__method_info_connect_profile_IN_ARG_pointers, + NULL, + NULL + }, + "handle-connect-profile", + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_device__method_info_disconnect_profile_IN_ARG_UUID = +{ + { + -1, + (gchar *) "UUID", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_device__method_info_disconnect_profile_IN_ARG_pointers[] = +{ + &_bluez_device__method_info_disconnect_profile_IN_ARG_UUID, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_device__method_info_disconnect_profile = +{ + { + -1, + (gchar *) "DisconnectProfile", + (GDBusArgInfo **) &_bluez_device__method_info_disconnect_profile_IN_ARG_pointers, + NULL, + NULL + }, + "handle-disconnect-profile", + FALSE +}; + +static const _ExtendedGDBusMethodInfo _bluez_device__method_info_pair = +{ + { + -1, + (gchar *) "Pair", + NULL, + NULL, + NULL + }, + "handle-pair", + FALSE +}; + +static const _ExtendedGDBusMethodInfo _bluez_device__method_info_cancel_pairing = +{ + { + -1, + (gchar *) "CancelPairing", + NULL, + NULL, + NULL + }, + "handle-cancel-pairing", + FALSE +}; + +static const _ExtendedGDBusMethodInfo * const _bluez_device__method_info_pointers[] = +{ + &_bluez_device__method_info_disconnect, + &_bluez_device__method_info_connect, + &_bluez_device__method_info_connect_profile, + &_bluez_device__method_info_disconnect_profile, + &_bluez_device__method_info_pair, + &_bluez_device__method_info_cancel_pairing, + NULL +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_address = +{ + { + -1, + (gchar *) "Address", + (gchar *) "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "address", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_name = +{ + { + -1, + (gchar *) "Name", + (gchar *) "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "name", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_alias = +{ + { + -1, + (gchar *) "Alias", + (gchar *) "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE, + NULL + }, + "alias", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_class = +{ + { + -1, + (gchar *) "Class", + (gchar *) "u", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "class", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_appearance = +{ + { + -1, + (gchar *) "Appearance", + (gchar *) "q", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "appearance", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_icon = +{ + { + -1, + (gchar *) "Icon", + (gchar *) "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "icon", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_paired = +{ + { + -1, + (gchar *) "Paired", + (gchar *) "b", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "paired", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_trusted = +{ + { + -1, + (gchar *) "Trusted", + (gchar *) "b", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE, + NULL + }, + "trusted", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_blocked = +{ + { + -1, + (gchar *) "Blocked", + (gchar *) "b", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE, + NULL + }, + "blocked", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_legacy_pairing = +{ + { + -1, + (gchar *) "LegacyPairing", + (gchar *) "b", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "legacy-pairing", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_rssi = +{ + { + -1, + (gchar *) "RSSI", + (gchar *) "n", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "rssi", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_connected = +{ + { + -1, + (gchar *) "Connected", + (gchar *) "b", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "connected", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_uuids = +{ + { + -1, + (gchar *) "UUIDs", + (gchar *) "as", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "uuids", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_modalias = +{ + { + -1, + (gchar *) "Modalias", + (gchar *) "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "modalias", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_adapter = +{ + { + -1, + (gchar *) "Adapter", + (gchar *) "o", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "adapter", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_manufacturer_data = +{ + { + -1, + (gchar *) "ManufacturerData", + (gchar *) "a{qv}", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "manufacturer-data", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_service_data = +{ + { + -1, + (gchar *) "ServiceData", + (gchar *) "a{sv}", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "service-data", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_tx_power = +{ + { + -1, + (gchar *) "TxPower", + (gchar *) "n", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "tx-power", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device__property_info_gatt_services = +{ + { + -1, + (gchar *) "GattServices", + (gchar *) "ao", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "gatt-services", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo * const _bluez_device__property_info_pointers[] = +{ + &_bluez_device__property_info_address, + &_bluez_device__property_info_name, + &_bluez_device__property_info_alias, + &_bluez_device__property_info_class, + &_bluez_device__property_info_appearance, + &_bluez_device__property_info_icon, + &_bluez_device__property_info_paired, + &_bluez_device__property_info_trusted, + &_bluez_device__property_info_blocked, + &_bluez_device__property_info_legacy_pairing, + &_bluez_device__property_info_rssi, + &_bluez_device__property_info_connected, + &_bluez_device__property_info_uuids, + &_bluez_device__property_info_modalias, + &_bluez_device__property_info_adapter, + &_bluez_device__property_info_manufacturer_data, + &_bluez_device__property_info_service_data, + &_bluez_device__property_info_tx_power, + &_bluez_device__property_info_gatt_services, + NULL +}; + +static const _ExtendedGDBusInterfaceInfo _bluez_device__interface_info = +{ + { + -1, + (gchar *) "org.bluez.Device1", + (GDBusMethodInfo **) &_bluez_device__method_info_pointers, + NULL, + (GDBusPropertyInfo **) &_bluez_device__property_info_pointers, + NULL + }, + "", +}; + + +/** + * bluez_device__interface_info: + * + * Gets a machine-readable description of the org.bluez.Device1 D-Bus interface. + * + * Returns: (transfer none): A #GDBusInterfaceInfo. Do not free. + */ +GDBusInterfaceInfo * +bluez_device__interface_info (void) +{ + return (GDBusInterfaceInfo *) &_bluez_device__interface_info.parent_struct; +} + +/** + * bluez_device__override_properties: + * @klass: The class structure for a #GObject-derived class. + * @property_id_begin: The property id to assign to the first overridden property. + * + * Overrides all #GObject properties in the #bluezdevice interface for a concrete class. + * The properties are overridden in the order they are defined. + * + * Returns: The last property id. + */ +guint +bluez_device__override_properties (GObjectClass *klass, guint property_id_begin) +{ + g_object_class_override_property (klass, property_id_begin++, "address"); + g_object_class_override_property (klass, property_id_begin++, "name"); + g_object_class_override_property (klass, property_id_begin++, "alias"); + g_object_class_override_property (klass, property_id_begin++, "class"); + g_object_class_override_property (klass, property_id_begin++, "appearance"); + g_object_class_override_property (klass, property_id_begin++, "icon"); + g_object_class_override_property (klass, property_id_begin++, "paired"); + g_object_class_override_property (klass, property_id_begin++, "trusted"); + g_object_class_override_property (klass, property_id_begin++, "blocked"); + g_object_class_override_property (klass, property_id_begin++, "legacy-pairing"); + g_object_class_override_property (klass, property_id_begin++, "rssi"); + g_object_class_override_property (klass, property_id_begin++, "connected"); + g_object_class_override_property (klass, property_id_begin++, "uuids"); + g_object_class_override_property (klass, property_id_begin++, "modalias"); + g_object_class_override_property (klass, property_id_begin++, "adapter"); + g_object_class_override_property (klass, property_id_begin++, "manufacturer-data"); + g_object_class_override_property (klass, property_id_begin++, "service-data"); + g_object_class_override_property (klass, property_id_begin++, "tx-power"); + g_object_class_override_property (klass, property_id_begin++, "gatt-services"); + return property_id_begin - 1; +} + + + +/** + * bluezdevice: + * + * Abstract interface type for the D-Bus interface org.bluez.Device1. + */ + +/** + * bluezdeviceIface: + * @parent_iface: The parent interface. + * @handle_cancel_pairing: Handler for the #bluezdevice::handle-cancel-pairing signal. + * @handle_connect: Handler for the #bluezdevice::handle-connect signal. + * @handle_connect_profile: Handler for the #bluezdevice::handle-connect-profile signal. + * @handle_disconnect: Handler for the #bluezdevice::handle-disconnect signal. + * @handle_disconnect_profile: Handler for the #bluezdevice::handle-disconnect-profile signal. + * @handle_pair: Handler for the #bluezdevice::handle-pair signal. + * @get_adapter: Getter for the #bluezdevice:adapter property. + * @get_address: Getter for the #bluezdevice:address property. + * @get_alias: Getter for the #bluezdevice:alias property. + * @get_appearance: Getter for the #bluezdevice:appearance property. + * @get_blocked: Getter for the #bluezdevice:blocked property. + * @get_class: Getter for the #bluezdevice:class property. + * @get_connected: Getter for the #bluezdevice:connected property. + * @get_gatt_services: Getter for the #bluezdevice:gatt-services property. + * @get_icon: Getter for the #bluezdevice:icon property. + * @get_legacy_pairing: Getter for the #bluezdevice:legacy-pairing property. + * @get_manufacturer_data: Getter for the #bluezdevice:manufacturer-data property. + * @get_modalias: Getter for the #bluezdevice:modalias property. + * @get_name: Getter for the #bluezdevice:name property. + * @get_paired: Getter for the #bluezdevice:paired property. + * @get_rssi: Getter for the #bluezdevice:rssi property. + * @get_service_data: Getter for the #bluezdevice:service-data property. + * @get_trusted: Getter for the #bluezdevice:trusted property. + * @get_tx_power: Getter for the #bluezdevice:tx-power property. + * @get_uuids: Getter for the #bluezdevice:uuids property. + * + * Virtual table for the D-Bus interface org.bluez.Device1. + */ + +typedef bluezdeviceIface bluezdeviceInterface; +G_DEFINE_INTERFACE (bluezdevice, bluez_device_, G_TYPE_OBJECT); + +static void +bluez_device__default_init (bluezdeviceIface *iface) +{ + /* GObject signals for incoming D-Bus method calls: */ + /** + * bluezdevice::handle-disconnect: + * @object: A #bluezdevice. + * @invocation: A #GDBusMethodInvocation. + * + * Signal emitted when a remote caller is invoking the Disconnect() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_device__complete_disconnect() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-disconnect", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezdeviceIface, handle_disconnect), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 1, + G_TYPE_DBUS_METHOD_INVOCATION); + + /** + * bluezdevice::handle-connect: + * @object: A #bluezdevice. + * @invocation: A #GDBusMethodInvocation. + * + * Signal emitted when a remote caller is invoking the Connect() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_device__complete_connect() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-connect", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezdeviceIface, handle_connect), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 1, + G_TYPE_DBUS_METHOD_INVOCATION); + + /** + * bluezdevice::handle-connect-profile: + * @object: A #bluezdevice. + * @invocation: A #GDBusMethodInvocation. + * @arg_UUID: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the ConnectProfile() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_device__complete_connect_profile() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-connect-profile", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezdeviceIface, handle_connect_profile), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 2, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING); + + /** + * bluezdevice::handle-disconnect-profile: + * @object: A #bluezdevice. + * @invocation: A #GDBusMethodInvocation. + * @arg_UUID: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the DisconnectProfile() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_device__complete_disconnect_profile() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-disconnect-profile", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezdeviceIface, handle_disconnect_profile), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 2, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING); + + /** + * bluezdevice::handle-pair: + * @object: A #bluezdevice. + * @invocation: A #GDBusMethodInvocation. + * + * Signal emitted when a remote caller is invoking the Pair() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_device__complete_pair() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-pair", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezdeviceIface, handle_pair), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 1, + G_TYPE_DBUS_METHOD_INVOCATION); + + /** + * bluezdevice::handle-cancel-pairing: + * @object: A #bluezdevice. + * @invocation: A #GDBusMethodInvocation. + * + * Signal emitted when a remote caller is invoking the CancelPairing() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_device__complete_cancel_pairing() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-cancel-pairing", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezdeviceIface, handle_cancel_pairing), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 1, + G_TYPE_DBUS_METHOD_INVOCATION); + + /* GObject properties for D-Bus properties: */ + /** + * bluezdevice:address: + * + * Represents the D-Bus property "Address". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("address", "Address", "Address", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:name: + * + * Represents the D-Bus property "Name". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("name", "Name", "Name", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:alias: + * + * Represents the D-Bus property "Alias". + * + * Since the D-Bus property for this #GObject property is both readable and writable, it is meaningful to both read from it and write to it on both the service- and client-side. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("alias", "Alias", "Alias", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:class: + * + * Represents the D-Bus property "Class". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_uint ("class", "Class", "Class", 0, G_MAXUINT32, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:appearance: + * + * Represents the D-Bus property "Appearance". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_uint ("appearance", "Appearance", "Appearance", 0, G_MAXUINT16, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:icon: + * + * Represents the D-Bus property "Icon". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("icon", "Icon", "Icon", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:paired: + * + * Represents the D-Bus property "Paired". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_boolean ("paired", "Paired", "Paired", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:trusted: + * + * Represents the D-Bus property "Trusted". + * + * Since the D-Bus property for this #GObject property is both readable and writable, it is meaningful to both read from it and write to it on both the service- and client-side. + */ + g_object_interface_install_property (iface, + g_param_spec_boolean ("trusted", "Trusted", "Trusted", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:blocked: + * + * Represents the D-Bus property "Blocked". + * + * Since the D-Bus property for this #GObject property is both readable and writable, it is meaningful to both read from it and write to it on both the service- and client-side. + */ + g_object_interface_install_property (iface, + g_param_spec_boolean ("blocked", "Blocked", "Blocked", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:legacy-pairing: + * + * Represents the D-Bus property "LegacyPairing". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_boolean ("legacy-pairing", "LegacyPairing", "LegacyPairing", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:rssi: + * + * Represents the D-Bus property "RSSI". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_int ("rssi", "RSSI", "RSSI", G_MININT16, G_MAXINT16, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:connected: + * + * Represents the D-Bus property "Connected". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_boolean ("connected", "Connected", "Connected", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:uuids: + * + * Represents the D-Bus property "UUIDs". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_boxed ("uuids", "UUIDs", "UUIDs", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:modalias: + * + * Represents the D-Bus property "Modalias". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("modalias", "Modalias", "Modalias", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:adapter: + * + * Represents the D-Bus property "Adapter". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("adapter", "Adapter", "Adapter", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:manufacturer-data: + * + * Represents the D-Bus property "ManufacturerData". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_variant ("manufacturer-data", "ManufacturerData", "ManufacturerData", G_VARIANT_TYPE ("a{qv}"), NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:service-data: + * + * Represents the D-Bus property "ServiceData". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_variant ("service-data", "ServiceData", "ServiceData", G_VARIANT_TYPE ("a{sv}"), NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:tx-power: + * + * Represents the D-Bus property "TxPower". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_int ("tx-power", "TxPower", "TxPower", G_MININT16, G_MAXINT16, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdevice:gatt-services: + * + * Represents the D-Bus property "GattServices". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_boxed ("gatt-services", "GattServices", "GattServices", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +/** + * bluez_device__get_address: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Address" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device__dup_address() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar * +bluez_device__get_address (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_address (object); +} + +/** + * bluez_device__dup_address: (skip) + * @object: A #bluezdevice. + * + * Gets a copy of the "Address" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_free(). + */ +gchar * +bluez_device__dup_address (bluezdevice *object) +{ + gchar *value; + g_object_get (G_OBJECT (object), "address", &value, NULL); + return value; +} + +/** + * bluez_device__set_address: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Address" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_address (bluezdevice *object, const gchar *value) +{ + g_object_set (G_OBJECT (object), "address", value, NULL); +} + +/** + * bluez_device__get_name: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Name" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device__dup_name() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar * +bluez_device__get_name (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_name (object); +} + +/** + * bluez_device__dup_name: (skip) + * @object: A #bluezdevice. + * + * Gets a copy of the "Name" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_free(). + */ +gchar * +bluez_device__dup_name (bluezdevice *object) +{ + gchar *value; + g_object_get (G_OBJECT (object), "name", &value, NULL); + return value; +} + +/** + * bluez_device__set_name: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Name" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_name (bluezdevice *object, const gchar *value) +{ + g_object_set (G_OBJECT (object), "name", value, NULL); +} + +/** + * bluez_device__get_alias: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Alias" D-Bus property. + * + * Since this D-Bus property is both readable and writable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device__dup_alias() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar * +bluez_device__get_alias (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_alias (object); +} + +/** + * bluez_device__dup_alias: (skip) + * @object: A #bluezdevice. + * + * Gets a copy of the "Alias" D-Bus property. + * + * Since this D-Bus property is both readable and writable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_free(). + */ +gchar * +bluez_device__dup_alias (bluezdevice *object) +{ + gchar *value; + g_object_get (G_OBJECT (object), "alias", &value, NULL); + return value; +} + +/** + * bluez_device__set_alias: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Alias" D-Bus property to @value. + * + * Since this D-Bus property is both readable and writable, it is meaningful to use this function on both the client- and service-side. + */ +void +bluez_device__set_alias (bluezdevice *object, const gchar *value) +{ + g_object_set (G_OBJECT (object), "alias", value, NULL); +} + +/** + * bluez_device__get_class: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Class" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: The property value. + */ +guint +bluez_device__get_class (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_class (object); +} + +/** + * bluez_device__set_class: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Class" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_class (bluezdevice *object, guint value) +{ + g_object_set (G_OBJECT (object), "class", value, NULL); +} + +/** + * bluez_device__get_appearance: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Appearance" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: The property value. + */ +guint16 +bluez_device__get_appearance (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_appearance (object); +} + +/** + * bluez_device__set_appearance: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Appearance" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_appearance (bluezdevice *object, guint16 value) +{ + g_object_set (G_OBJECT (object), "appearance", value, NULL); +} + +/** + * bluez_device__get_icon: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Icon" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device__dup_icon() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar * +bluez_device__get_icon (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_icon (object); +} + +/** + * bluez_device__dup_icon: (skip) + * @object: A #bluezdevice. + * + * Gets a copy of the "Icon" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_free(). + */ +gchar * +bluez_device__dup_icon (bluezdevice *object) +{ + gchar *value; + g_object_get (G_OBJECT (object), "icon", &value, NULL); + return value; +} + +/** + * bluez_device__set_icon: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Icon" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_icon (bluezdevice *object, const gchar *value) +{ + g_object_set (G_OBJECT (object), "icon", value, NULL); +} + +/** + * bluez_device__get_paired: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Paired" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: The property value. + */ +gboolean +bluez_device__get_paired (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_paired (object); +} + +/** + * bluez_device__set_paired: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Paired" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_paired (bluezdevice *object, gboolean value) +{ + g_object_set (G_OBJECT (object), "paired", value, NULL); +} + +/** + * bluez_device__get_trusted: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Trusted" D-Bus property. + * + * Since this D-Bus property is both readable and writable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: The property value. + */ +gboolean +bluez_device__get_trusted (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_trusted (object); +} + +/** + * bluez_device__set_trusted: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Trusted" D-Bus property to @value. + * + * Since this D-Bus property is both readable and writable, it is meaningful to use this function on both the client- and service-side. + */ +void +bluez_device__set_trusted (bluezdevice *object, gboolean value) +{ + g_object_set (G_OBJECT (object), "trusted", value, NULL); +} + +/** + * bluez_device__get_blocked: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Blocked" D-Bus property. + * + * Since this D-Bus property is both readable and writable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: The property value. + */ +gboolean +bluez_device__get_blocked (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_blocked (object); +} + +/** + * bluez_device__set_blocked: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Blocked" D-Bus property to @value. + * + * Since this D-Bus property is both readable and writable, it is meaningful to use this function on both the client- and service-side. + */ +void +bluez_device__set_blocked (bluezdevice *object, gboolean value) +{ + g_object_set (G_OBJECT (object), "blocked", value, NULL); +} + +/** + * bluez_device__get_legacy_pairing: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "LegacyPairing" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: The property value. + */ +gboolean +bluez_device__get_legacy_pairing (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_legacy_pairing (object); +} + +/** + * bluez_device__set_legacy_pairing: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "LegacyPairing" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_legacy_pairing (bluezdevice *object, gboolean value) +{ + g_object_set (G_OBJECT (object), "legacy-pairing", value, NULL); +} + +/** + * bluez_device__get_rssi: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "RSSI" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: The property value. + */ +gint16 +bluez_device__get_rssi (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_rssi (object); +} + +/** + * bluez_device__set_rssi: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "RSSI" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_rssi (bluezdevice *object, gint16 value) +{ + g_object_set (G_OBJECT (object), "rssi", value, NULL); +} + +/** + * bluez_device__get_connected: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Connected" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: The property value. + */ +gboolean +bluez_device__get_connected (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_connected (object); +} + +/** + * bluez_device__set_connected: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Connected" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_connected (bluezdevice *object, gboolean value) +{ + g_object_set (G_OBJECT (object), "connected", value, NULL); +} + +/** + * bluez_device__get_uuids: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "UUIDs" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device__dup_uuids() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar *const * +bluez_device__get_uuids (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_uuids (object); +} + +/** + * bluez_device__dup_uuids: (skip) + * @object: A #bluezdevice. + * + * Gets a copy of the "UUIDs" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_strfreev(). + */ +gchar ** +bluez_device__dup_uuids (bluezdevice *object) +{ + gchar **value; + g_object_get (G_OBJECT (object), "uuids", &value, NULL); + return value; +} + +/** + * bluez_device__set_uuids: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "UUIDs" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_uuids (bluezdevice *object, const gchar *const *value) +{ + g_object_set (G_OBJECT (object), "uuids", value, NULL); +} + +/** + * bluez_device__get_modalias: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Modalias" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device__dup_modalias() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar * +bluez_device__get_modalias (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_modalias (object); +} + +/** + * bluez_device__dup_modalias: (skip) + * @object: A #bluezdevice. + * + * Gets a copy of the "Modalias" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_free(). + */ +gchar * +bluez_device__dup_modalias (bluezdevice *object) +{ + gchar *value; + g_object_get (G_OBJECT (object), "modalias", &value, NULL); + return value; +} + +/** + * bluez_device__set_modalias: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Modalias" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_modalias (bluezdevice *object, const gchar *value) +{ + g_object_set (G_OBJECT (object), "modalias", value, NULL); +} + +/** + * bluez_device__get_adapter: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "Adapter" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device__dup_adapter() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar * +bluez_device__get_adapter (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_adapter (object); +} + +/** + * bluez_device__dup_adapter: (skip) + * @object: A #bluezdevice. + * + * Gets a copy of the "Adapter" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_free(). + */ +gchar * +bluez_device__dup_adapter (bluezdevice *object) +{ + gchar *value; + g_object_get (G_OBJECT (object), "adapter", &value, NULL); + return value; +} + +/** + * bluez_device__set_adapter: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "Adapter" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_adapter (bluezdevice *object, const gchar *value) +{ + g_object_set (G_OBJECT (object), "adapter", value, NULL); +} + +/** + * bluez_device__get_manufacturer_data: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "ManufacturerData" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device__dup_manufacturer_data() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +GVariant * +bluez_device__get_manufacturer_data (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_manufacturer_data (object); +} + +/** + * bluez_device__dup_manufacturer_data: (skip) + * @object: A #bluezdevice. + * + * Gets a copy of the "ManufacturerData" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_variant_unref(). + */ +GVariant * +bluez_device__dup_manufacturer_data (bluezdevice *object) +{ + GVariant *value; + g_object_get (G_OBJECT (object), "manufacturer-data", &value, NULL); + return value; +} + +/** + * bluez_device__set_manufacturer_data: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "ManufacturerData" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_manufacturer_data (bluezdevice *object, GVariant *value) +{ + g_object_set (G_OBJECT (object), "manufacturer-data", value, NULL); +} + +/** + * bluez_device__get_service_data: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "ServiceData" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device__dup_service_data() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +GVariant * +bluez_device__get_service_data (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_service_data (object); +} + +/** + * bluez_device__dup_service_data: (skip) + * @object: A #bluezdevice. + * + * Gets a copy of the "ServiceData" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_variant_unref(). + */ +GVariant * +bluez_device__dup_service_data (bluezdevice *object) +{ + GVariant *value; + g_object_get (G_OBJECT (object), "service-data", &value, NULL); + return value; +} + +/** + * bluez_device__set_service_data: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "ServiceData" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_service_data (bluezdevice *object, GVariant *value) +{ + g_object_set (G_OBJECT (object), "service-data", value, NULL); +} + +/** + * bluez_device__get_tx_power: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "TxPower" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: The property value. + */ +gint16 +bluez_device__get_tx_power (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_tx_power (object); +} + +/** + * bluez_device__set_tx_power: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "TxPower" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_tx_power (bluezdevice *object, gint16 value) +{ + g_object_set (G_OBJECT (object), "tx-power", value, NULL); +} + +/** + * bluez_device__get_gatt_services: (skip) + * @object: A #bluezdevice. + * + * Gets the value of the "GattServices" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device__dup_gatt_services() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar *const * +bluez_device__get_gatt_services (bluezdevice *object) +{ + return BLUEZ_DEVICE__GET_IFACE (object)->get_gatt_services (object); +} + +/** + * bluez_device__dup_gatt_services: (skip) + * @object: A #bluezdevice. + * + * Gets a copy of the "GattServices" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_strfreev(). + */ +gchar ** +bluez_device__dup_gatt_services (bluezdevice *object) +{ + gchar **value; + g_object_get (G_OBJECT (object), "gatt-services", &value, NULL); + return value; +} + +/** + * bluez_device__set_gatt_services: (skip) + * @object: A #bluezdevice. + * @value: The value to set. + * + * Sets the "GattServices" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device__set_gatt_services (bluezdevice *object, const gchar *const *value) +{ + g_object_set (G_OBJECT (object), "gatt-services", value, NULL); +} + +/** + * bluez_device__call_disconnect: + * @proxy: A #bluezdeviceProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the Disconnect() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device__call_disconnect_finish() to get the result of the operation. + * + * See bluez_device__call_disconnect_sync() for the synchronous, blocking version of this method. + */ +void +bluez_device__call_disconnect ( + bluezdevice *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "Disconnect", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_device__call_disconnect_finish: + * @proxy: A #bluezdeviceProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device__call_disconnect(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_device__call_disconnect(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_disconnect_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__call_disconnect_sync: + * @proxy: A #bluezdeviceProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the Disconnect() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_device__call_disconnect() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_disconnect_sync ( + bluezdevice *proxy, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "Disconnect", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__call_connect: + * @proxy: A #bluezdeviceProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the Connect() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device__call_connect_finish() to get the result of the operation. + * + * See bluez_device__call_connect_sync() for the synchronous, blocking version of this method. + */ +void +bluez_device__call_connect ( + bluezdevice *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "Connect", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_device__call_connect_finish: + * @proxy: A #bluezdeviceProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device__call_connect(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_device__call_connect(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_connect_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__call_connect_sync: + * @proxy: A #bluezdeviceProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the Connect() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_device__call_connect() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_connect_sync ( + bluezdevice *proxy, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "Connect", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__call_connect_profile: + * @proxy: A #bluezdeviceProxy. + * @arg_UUID: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the ConnectProfile() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device__call_connect_profile_finish() to get the result of the operation. + * + * See bluez_device__call_connect_profile_sync() for the synchronous, blocking version of this method. + */ +void +bluez_device__call_connect_profile ( + bluezdevice *proxy, + const gchar *arg_UUID, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "ConnectProfile", + g_variant_new ("(s)", + arg_UUID), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_device__call_connect_profile_finish: + * @proxy: A #bluezdeviceProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device__call_connect_profile(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_device__call_connect_profile(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_connect_profile_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__call_connect_profile_sync: + * @proxy: A #bluezdeviceProxy. + * @arg_UUID: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the ConnectProfile() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_device__call_connect_profile() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_connect_profile_sync ( + bluezdevice *proxy, + const gchar *arg_UUID, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "ConnectProfile", + g_variant_new ("(s)", + arg_UUID), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__call_disconnect_profile: + * @proxy: A #bluezdeviceProxy. + * @arg_UUID: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the DisconnectProfile() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device__call_disconnect_profile_finish() to get the result of the operation. + * + * See bluez_device__call_disconnect_profile_sync() for the synchronous, blocking version of this method. + */ +void +bluez_device__call_disconnect_profile ( + bluezdevice *proxy, + const gchar *arg_UUID, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "DisconnectProfile", + g_variant_new ("(s)", + arg_UUID), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_device__call_disconnect_profile_finish: + * @proxy: A #bluezdeviceProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device__call_disconnect_profile(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_device__call_disconnect_profile(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_disconnect_profile_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__call_disconnect_profile_sync: + * @proxy: A #bluezdeviceProxy. + * @arg_UUID: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the DisconnectProfile() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_device__call_disconnect_profile() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_disconnect_profile_sync ( + bluezdevice *proxy, + const gchar *arg_UUID, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "DisconnectProfile", + g_variant_new ("(s)", + arg_UUID), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__call_pair: + * @proxy: A #bluezdeviceProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the Pair() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device__call_pair_finish() to get the result of the operation. + * + * See bluez_device__call_pair_sync() for the synchronous, blocking version of this method. + */ +void +bluez_device__call_pair ( + bluezdevice *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "Pair", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_device__call_pair_finish: + * @proxy: A #bluezdeviceProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device__call_pair(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_device__call_pair(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_pair_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__call_pair_sync: + * @proxy: A #bluezdeviceProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the Pair() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_device__call_pair() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_pair_sync ( + bluezdevice *proxy, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "Pair", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__call_cancel_pairing: + * @proxy: A #bluezdeviceProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the CancelPairing() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device__call_cancel_pairing_finish() to get the result of the operation. + * + * See bluez_device__call_cancel_pairing_sync() for the synchronous, blocking version of this method. + */ +void +bluez_device__call_cancel_pairing ( + bluezdevice *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "CancelPairing", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_device__call_cancel_pairing_finish: + * @proxy: A #bluezdeviceProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device__call_cancel_pairing(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_device__call_cancel_pairing(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_cancel_pairing_finish ( + bluezdevice *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__call_cancel_pairing_sync: + * @proxy: A #bluezdeviceProxy. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the CancelPairing() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_device__call_cancel_pairing() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device__call_cancel_pairing_sync ( + bluezdevice *proxy, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "CancelPairing", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device__complete_disconnect: + * @object: A #bluezdevice. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the Disconnect() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_device__complete_disconnect ( + bluezdevice *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/** + * bluez_device__complete_connect: + * @object: A #bluezdevice. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the Connect() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_device__complete_connect ( + bluezdevice *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/** + * bluez_device__complete_connect_profile: + * @object: A #bluezdevice. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the ConnectProfile() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_device__complete_connect_profile ( + bluezdevice *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/** + * bluez_device__complete_disconnect_profile: + * @object: A #bluezdevice. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the DisconnectProfile() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_device__complete_disconnect_profile ( + bluezdevice *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/** + * bluez_device__complete_pair: + * @object: A #bluezdevice. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the Pair() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_device__complete_pair ( + bluezdevice *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/** + * bluez_device__complete_cancel_pairing: + * @object: A #bluezdevice. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the CancelPairing() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_device__complete_cancel_pairing ( + bluezdevice *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/* ------------------------------------------------------------------------ */ + +/** + * bluezdeviceProxy: + * + * The #bluezdeviceProxy structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezdeviceProxyClass: + * @parent_class: The parent class. + * + * Class structure for #bluezdeviceProxy. + */ + +struct _bluezdeviceProxyPrivate +{ + GData *qdata; +}; + +static void bluez_device__proxy_iface_init (bluezdeviceIface *iface); + +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezdeviceProxy, bluez_device__proxy, G_TYPE_DBUS_PROXY, + G_ADD_PRIVATE (bluezdeviceProxy) + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_, bluez_device__proxy_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezdeviceProxy, bluez_device__proxy, G_TYPE_DBUS_PROXY, + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_, bluez_device__proxy_iface_init)); + +#endif +static void +bluez_device__proxy_finalize (GObject *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + g_datalist_clear (&proxy->priv->qdata); + G_OBJECT_CLASS (bluez_device__proxy_parent_class)->finalize (object); +} + +static void +bluez_device__proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ + const _ExtendedGDBusPropertyInfo *info; + GVariant *variant; + g_assert (prop_id != 0 && prop_id - 1 < 19); + info = _bluez_device__property_info_pointers[prop_id - 1]; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (object), info->parent_struct.name); + if (info->use_gvariant) + { + g_value_set_variant (value, variant); + } + else + { + if (variant != NULL) + g_dbus_gvariant_to_gvalue (variant, value); + } + if (variant != NULL) + g_variant_unref (variant); +} + +static void +bluez_device__proxy_set_property_cb (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + const _ExtendedGDBusPropertyInfo *info = user_data; + GError *error; + GVariant *_ret; + error = NULL; + _ret = g_dbus_proxy_call_finish (proxy, res, &error); + if (!_ret) + { + g_warning ("Error setting property '%s' on interface org.bluez.Device1: %s (%s, %d)", + info->parent_struct.name, + error->message, g_quark_to_string (error->domain), error->code); + g_error_free (error); + } + else + { + g_variant_unref (_ret); + } +} + +static void +bluez_device__proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ + const _ExtendedGDBusPropertyInfo *info; + GVariant *variant; + g_assert (prop_id != 0 && prop_id - 1 < 19); + info = _bluez_device__property_info_pointers[prop_id - 1]; + variant = g_dbus_gvalue_to_gvariant (value, G_VARIANT_TYPE (info->parent_struct.signature)); + g_dbus_proxy_call (G_DBUS_PROXY (object), + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", "org.bluez.Device1", info->parent_struct.name, variant), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, (GAsyncReadyCallback) bluez_device__proxy_set_property_cb, (GDBusPropertyInfo *) &info->parent_struct); + g_variant_unref (variant); +} + +static void +bluez_device__proxy_g_signal (GDBusProxy *proxy, + const gchar *sender_name G_GNUC_UNUSED, + const gchar *signal_name, + GVariant *parameters) +{ + _ExtendedGDBusSignalInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint n; + guint signal_id; + info = (_ExtendedGDBusSignalInfo *) g_dbus_interface_info_lookup_signal ((GDBusInterfaceInfo *) &_bluez_device__interface_info.parent_struct, signal_name); + if (info == NULL) + return; + num_params = g_variant_n_children (parameters); + paramv = g_new0 (GValue, num_params + 1); + g_value_init (¶mv[0], BLUEZ_DEVICE_TYPE_); + g_value_set_object (¶mv[0], proxy); + g_variant_iter_init (&iter, parameters); + n = 1; + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.args[n - 1]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_DEVICE_TYPE_); + g_signal_emitv (paramv, signal_id, 0, NULL); + for (n = 0; n < num_params + 1; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static void +bluez_device__proxy_g_properties_changed (GDBusProxy *_proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (_proxy); + guint n; + const gchar *key; + GVariantIter *iter; + _ExtendedGDBusPropertyInfo *info; + g_variant_get (changed_properties, "a{sv}", &iter); + while (g_variant_iter_next (iter, "{&sv}", &key, NULL)) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device__interface_info.parent_struct, key); + g_datalist_remove_data (&proxy->priv->qdata, key); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } + g_variant_iter_free (iter); + for (n = 0; invalidated_properties[n] != NULL; n++) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device__interface_info.parent_struct, invalidated_properties[n]); + g_datalist_remove_data (&proxy->priv->qdata, invalidated_properties[n]); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } +} + +static const gchar * +bluez_device__proxy_get_address (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + const gchar *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Address"); + if (variant != NULL) + { + value = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static const gchar * +bluez_device__proxy_get_name (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + const gchar *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Name"); + if (variant != NULL) + { + value = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static const gchar * +bluez_device__proxy_get_alias (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + const gchar *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Alias"); + if (variant != NULL) + { + value = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static guint +bluez_device__proxy_get_class (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + guint value = 0; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Class"); + if (variant != NULL) + { + value = g_variant_get_uint32 (variant); + g_variant_unref (variant); + } + return value; +} + +static guint16 +bluez_device__proxy_get_appearance (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + guint16 value = 0; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Appearance"); + if (variant != NULL) + { + value = g_variant_get_uint16 (variant); + g_variant_unref (variant); + } + return value; +} + +static const gchar * +bluez_device__proxy_get_icon (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + const gchar *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Icon"); + if (variant != NULL) + { + value = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static gboolean +bluez_device__proxy_get_paired (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + gboolean value = 0; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Paired"); + if (variant != NULL) + { + value = g_variant_get_boolean (variant); + g_variant_unref (variant); + } + return value; +} + +static gboolean +bluez_device__proxy_get_trusted (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + gboolean value = 0; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Trusted"); + if (variant != NULL) + { + value = g_variant_get_boolean (variant); + g_variant_unref (variant); + } + return value; +} + +static gboolean +bluez_device__proxy_get_blocked (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + gboolean value = 0; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Blocked"); + if (variant != NULL) + { + value = g_variant_get_boolean (variant); + g_variant_unref (variant); + } + return value; +} + +static gboolean +bluez_device__proxy_get_legacy_pairing (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + gboolean value = 0; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "LegacyPairing"); + if (variant != NULL) + { + value = g_variant_get_boolean (variant); + g_variant_unref (variant); + } + return value; +} + +static gint16 +bluez_device__proxy_get_rssi (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + gint16 value = 0; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "RSSI"); + if (variant != NULL) + { + value = g_variant_get_int16 (variant); + g_variant_unref (variant); + } + return value; +} + +static gboolean +bluez_device__proxy_get_connected (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + gboolean value = 0; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Connected"); + if (variant != NULL) + { + value = g_variant_get_boolean (variant); + g_variant_unref (variant); + } + return value; +} + +static const gchar *const * +bluez_device__proxy_get_uuids (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + const gchar *const *value = NULL; + value = g_datalist_get_data (&proxy->priv->qdata, "UUIDs"); + if (value != NULL) + return value; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "UUIDs"); + if (variant != NULL) + { + value = g_variant_get_strv (variant, NULL); + g_datalist_set_data_full (&proxy->priv->qdata, "UUIDs", (gpointer) value, g_free); + g_variant_unref (variant); + } + return value; +} + +static const gchar * +bluez_device__proxy_get_modalias (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + const gchar *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Modalias"); + if (variant != NULL) + { + value = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static const gchar * +bluez_device__proxy_get_adapter (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + const gchar *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Adapter"); + if (variant != NULL) + { + value = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static GVariant * +bluez_device__proxy_get_manufacturer_data (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + GVariant *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "ManufacturerData"); + value = variant; + if (variant != NULL) + g_variant_unref (variant); + return value; +} + +static GVariant * +bluez_device__proxy_get_service_data (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + GVariant *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "ServiceData"); + value = variant; + if (variant != NULL) + g_variant_unref (variant); + return value; +} + +static gint16 +bluez_device__proxy_get_tx_power (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + gint16 value = 0; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "TxPower"); + if (variant != NULL) + { + value = g_variant_get_int16 (variant); + g_variant_unref (variant); + } + return value; +} + +static const gchar *const * +bluez_device__proxy_get_gatt_services (bluezdevice *object) +{ + bluezdeviceProxy *proxy = BLUEZ_DEVICE__PROXY (object); + GVariant *variant; + const gchar *const *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "GattServices"); + if (variant != NULL) + { + value = g_variant_get_objv (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static void +bluez_device__proxy_init (bluezdeviceProxy *proxy) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + proxy->priv = bluez_device__proxy_get_instance_private (proxy); +#else + proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, BLUEZ_DEVICE_TYPE__PROXY, bluezdeviceProxyPrivate); +#endif + + g_dbus_proxy_set_interface_info (G_DBUS_PROXY (proxy), bluez_device__interface_info ()); +} + +static void +bluez_device__proxy_class_init (bluezdeviceProxyClass *klass) +{ + GObjectClass *gobject_class; + GDBusProxyClass *proxy_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_device__proxy_finalize; + gobject_class->get_property = bluez_device__proxy_get_property; + gobject_class->set_property = bluez_device__proxy_set_property; + + proxy_class = G_DBUS_PROXY_CLASS (klass); + proxy_class->g_signal = bluez_device__proxy_g_signal; + proxy_class->g_properties_changed = bluez_device__proxy_g_properties_changed; + + bluez_device__override_properties (gobject_class, 1); + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezdeviceProxyPrivate)); +#endif +} + +static void +bluez_device__proxy_iface_init (bluezdeviceIface *iface) +{ + iface->get_address = bluez_device__proxy_get_address; + iface->get_name = bluez_device__proxy_get_name; + iface->get_alias = bluez_device__proxy_get_alias; + iface->get_class = bluez_device__proxy_get_class; + iface->get_appearance = bluez_device__proxy_get_appearance; + iface->get_icon = bluez_device__proxy_get_icon; + iface->get_paired = bluez_device__proxy_get_paired; + iface->get_trusted = bluez_device__proxy_get_trusted; + iface->get_blocked = bluez_device__proxy_get_blocked; + iface->get_legacy_pairing = bluez_device__proxy_get_legacy_pairing; + iface->get_rssi = bluez_device__proxy_get_rssi; + iface->get_connected = bluez_device__proxy_get_connected; + iface->get_uuids = bluez_device__proxy_get_uuids; + iface->get_modalias = bluez_device__proxy_get_modalias; + iface->get_adapter = bluez_device__proxy_get_adapter; + iface->get_manufacturer_data = bluez_device__proxy_get_manufacturer_data; + iface->get_service_data = bluez_device__proxy_get_service_data; + iface->get_tx_power = bluez_device__proxy_get_tx_power; + iface->get_gatt_services = bluez_device__proxy_get_gatt_services; +} + +/** + * bluez_device__proxy_new: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Asynchronously creates a proxy for the D-Bus interface org.bluez.Device1. See g_dbus_proxy_new() for more details. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device__proxy_new_finish() to get the result of the operation. + * + * See bluez_device__proxy_new_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_device__proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_DEVICE_TYPE__PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.bluez.Device1", NULL); +} + +/** + * bluez_device__proxy_new_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device__proxy_new(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_device__proxy_new(). + * + * Returns: (transfer full) (type bluezdeviceProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdevice * +bluez_device__proxy_new_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_DEVICE_ (ret); + else + return NULL; +} + +/** + * bluez_device__proxy_new_sync: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Synchronously creates a proxy for the D-Bus interface org.bluez.Device1. See g_dbus_proxy_new_sync() for more details. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_device__proxy_new() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezdeviceProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdevice * +bluez_device__proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_DEVICE_TYPE__PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.bluez.Device1", NULL); + if (ret != NULL) + return BLUEZ_DEVICE_ (ret); + else + return NULL; +} + + +/** + * bluez_device__proxy_new_for_bus: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Like bluez_device__proxy_new() but takes a #GBusType instead of a #GDBusConnection. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device__proxy_new_for_bus_finish() to get the result of the operation. + * + * See bluez_device__proxy_new_for_bus_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_device__proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_DEVICE_TYPE__PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.bluez.Device1", NULL); +} + +/** + * bluez_device__proxy_new_for_bus_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device__proxy_new_for_bus(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_device__proxy_new_for_bus(). + * + * Returns: (transfer full) (type bluezdeviceProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdevice * +bluez_device__proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_DEVICE_ (ret); + else + return NULL; +} + +/** + * bluez_device__proxy_new_for_bus_sync: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Like bluez_device__proxy_new_sync() but takes a #GBusType instead of a #GDBusConnection. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_device__proxy_new_for_bus() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezdeviceProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdevice * +bluez_device__proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_DEVICE_TYPE__PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.bluez.Device1", NULL); + if (ret != NULL) + return BLUEZ_DEVICE_ (ret); + else + return NULL; +} + + +/* ------------------------------------------------------------------------ */ + +/** + * bluezdeviceSkeleton: + * + * The #bluezdeviceSkeleton structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezdeviceSkeletonClass: + * @parent_class: The parent class. + * + * Class structure for #bluezdeviceSkeleton. + */ + +struct _bluezdeviceSkeletonPrivate +{ + GValue *properties; + GList *changed_properties; + GSource *changed_properties_idle_source; + GMainContext *context; + GMutex lock; +}; + +static void +_bluez_device__skeleton_handle_method_call ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (user_data); + _ExtendedGDBusMethodInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint num_extra; + guint n; + guint signal_id; + GValue return_value = G_VALUE_INIT; + info = (_ExtendedGDBusMethodInfo *) g_dbus_method_invocation_get_method_info (invocation); + g_assert (info != NULL); + num_params = g_variant_n_children (parameters); + num_extra = info->pass_fdlist ? 3 : 2; paramv = g_new0 (GValue, num_params + num_extra); + n = 0; + g_value_init (¶mv[n], BLUEZ_DEVICE_TYPE_); + g_value_set_object (¶mv[n++], skeleton); + g_value_init (¶mv[n], G_TYPE_DBUS_METHOD_INVOCATION); + g_value_set_object (¶mv[n++], invocation); + if (info->pass_fdlist) + { +#ifdef G_OS_UNIX + g_value_init (¶mv[n], G_TYPE_UNIX_FD_LIST); + g_value_set_object (¶mv[n++], g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation))); +#else + g_assert_not_reached (); +#endif + } + g_variant_iter_init (&iter, parameters); + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.in_args[n - num_extra]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_DEVICE_TYPE_); + g_value_init (&return_value, G_TYPE_BOOLEAN); + g_signal_emitv (paramv, signal_id, 0, &return_value); + if (!g_value_get_boolean (&return_value)) + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method %s is not implemented on interface %s", method_name, interface_name); + g_value_unset (&return_value); + for (n = 0; n < num_params + num_extra; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static GVariant * +_bluez_device__skeleton_handle_get_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + GVariant *ret; + ret = NULL; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device__interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + g_value_init (&value, pspec->value_type); + g_object_get_property (G_OBJECT (skeleton), info->hyphen_name, &value); + ret = g_dbus_gvalue_to_gvariant (&value, G_VARIANT_TYPE (info->parent_struct.signature)); + g_value_unset (&value); + } + return ret; +} + +static gboolean +_bluez_device__skeleton_handle_set_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GVariant *variant, + GError **error, + gpointer user_data) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + gboolean ret; + ret = FALSE; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device__interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + if (info->use_gvariant) + g_value_set_variant (&value, variant); + else + g_dbus_gvariant_to_gvalue (variant, &value); + g_object_set_property (G_OBJECT (skeleton), info->hyphen_name, &value); + g_value_unset (&value); + ret = TRUE; + } + return ret; +} + +static const GDBusInterfaceVTable _bluez_device__skeleton_vtable = +{ + _bluez_device__skeleton_handle_method_call, + _bluez_device__skeleton_handle_get_property, + _bluez_device__skeleton_handle_set_property, + {NULL} +}; + +static GDBusInterfaceInfo * +bluez_device__skeleton_dbus_interface_get_info (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return bluez_device__interface_info (); +} + +static GDBusInterfaceVTable * +bluez_device__skeleton_dbus_interface_get_vtable (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return (GDBusInterfaceVTable *) &_bluez_device__skeleton_vtable; +} + +static GVariant * +bluez_device__skeleton_dbus_interface_get_properties (GDBusInterfaceSkeleton *_skeleton) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (_skeleton); + + GVariantBuilder builder; + guint n; + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + if (_bluez_device__interface_info.parent_struct.properties == NULL) + goto out; + for (n = 0; _bluez_device__interface_info.parent_struct.properties[n] != NULL; n++) + { + GDBusPropertyInfo *info = _bluez_device__interface_info.parent_struct.properties[n]; + if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) + { + GVariant *value; + value = _bluez_device__skeleton_handle_get_property (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)), NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.bluez.Device1", info->name, NULL, skeleton); + if (value != NULL) + { + g_variant_take_ref (value); + g_variant_builder_add (&builder, "{sv}", info->name, value); + g_variant_unref (value); + } + } + } +out: + return g_variant_builder_end (&builder); +} + +static gboolean _bluez_device__emit_changed (gpointer user_data); + +static void +bluez_device__skeleton_dbus_interface_flush (GDBusInterfaceSkeleton *_skeleton) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (_skeleton); + gboolean emit_changed = FALSE; + + g_mutex_lock (&skeleton->priv->lock); + if (skeleton->priv->changed_properties_idle_source != NULL) + { + g_source_destroy (skeleton->priv->changed_properties_idle_source); + skeleton->priv->changed_properties_idle_source = NULL; + emit_changed = TRUE; + } + g_mutex_unlock (&skeleton->priv->lock); + + if (emit_changed) + _bluez_device__emit_changed (skeleton); +} + +static void bluez_device__skeleton_iface_init (bluezdeviceIface *iface); +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezdeviceSkeleton, bluez_device__skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_ADD_PRIVATE (bluezdeviceSkeleton) + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_, bluez_device__skeleton_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezdeviceSkeleton, bluez_device__skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_, bluez_device__skeleton_iface_init)); + +#endif +static void +bluez_device__skeleton_finalize (GObject *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + guint n; + for (n = 0; n < 19; n++) + g_value_unset (&skeleton->priv->properties[n]); + g_free (skeleton->priv->properties); + g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free); + if (skeleton->priv->changed_properties_idle_source != NULL) + g_source_destroy (skeleton->priv->changed_properties_idle_source); + g_main_context_unref (skeleton->priv->context); + g_mutex_clear (&skeleton->priv->lock); + G_OBJECT_CLASS (bluez_device__skeleton_parent_class)->finalize (object); +} + +static void +bluez_device__skeleton_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + g_assert (prop_id != 0 && prop_id - 1 < 19); + g_mutex_lock (&skeleton->priv->lock); + g_value_copy (&skeleton->priv->properties[prop_id - 1], value); + g_mutex_unlock (&skeleton->priv->lock); +} + +static gboolean +_bluez_device__emit_changed (gpointer user_data) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (user_data); + GList *l; + GVariantBuilder builder; + GVariantBuilder invalidated_builder; + guint num_changes; + + g_mutex_lock (&skeleton->priv->lock); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as")); + for (l = skeleton->priv->changed_properties, num_changes = 0; l != NULL; l = l->next) + { + ChangedProperty *cp = l->data; + GVariant *variant; + const GValue *cur_value; + + cur_value = &skeleton->priv->properties[cp->prop_id - 1]; + if (!_g_value_equal (cur_value, &cp->orig_value)) + { + variant = g_dbus_gvalue_to_gvariant (cur_value, G_VARIANT_TYPE (cp->info->parent_struct.signature)); + g_variant_builder_add (&builder, "{sv}", cp->info->parent_struct.name, variant); + g_variant_unref (variant); + num_changes++; + } + } + if (num_changes > 0) + { + GList *connections, *ll; + GVariant *signal_variant; + signal_variant = g_variant_ref_sink (g_variant_new ("(sa{sv}as)", "org.bluez.Device1", + &builder, &invalidated_builder)); + connections = g_dbus_interface_skeleton_get_connections (G_DBUS_INTERFACE_SKELETON (skeleton)); + for (ll = connections; ll != NULL; ll = ll->next) + { + GDBusConnection *connection = ll->data; + + g_dbus_connection_emit_signal (connection, + NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + signal_variant, + NULL); + } + g_variant_unref (signal_variant); + g_list_free_full (connections, g_object_unref); + } + else + { + g_variant_builder_clear (&builder); + g_variant_builder_clear (&invalidated_builder); + } + g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free); + skeleton->priv->changed_properties = NULL; + skeleton->priv->changed_properties_idle_source = NULL; + g_mutex_unlock (&skeleton->priv->lock); + return FALSE; +} + +static void +_bluez_device__schedule_emit_changed (bluezdeviceSkeleton *skeleton, const _ExtendedGDBusPropertyInfo *info, guint prop_id, const GValue *orig_value) +{ + ChangedProperty *cp; + GList *l; + cp = NULL; + for (l = skeleton->priv->changed_properties; l != NULL; l = l->next) + { + ChangedProperty *i_cp = l->data; + if (i_cp->info == info) + { + cp = i_cp; + break; + } + } + if (cp == NULL) + { + cp = g_new0 (ChangedProperty, 1); + cp->prop_id = prop_id; + cp->info = info; + skeleton->priv->changed_properties = g_list_prepend (skeleton->priv->changed_properties, cp); + g_value_init (&cp->orig_value, G_VALUE_TYPE (orig_value)); + g_value_copy (orig_value, &cp->orig_value); + } +} + +static void +bluez_device__skeleton_notify (GObject *object, + GParamSpec *pspec G_GNUC_UNUSED) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + g_mutex_lock (&skeleton->priv->lock); + if (skeleton->priv->changed_properties != NULL && + skeleton->priv->changed_properties_idle_source == NULL) + { + skeleton->priv->changed_properties_idle_source = g_idle_source_new (); + g_source_set_priority (skeleton->priv->changed_properties_idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (skeleton->priv->changed_properties_idle_source, _bluez_device__emit_changed, g_object_ref (skeleton), (GDestroyNotify) g_object_unref); + g_source_attach (skeleton->priv->changed_properties_idle_source, skeleton->priv->context); + g_source_unref (skeleton->priv->changed_properties_idle_source); + } + g_mutex_unlock (&skeleton->priv->lock); +} + +static void +bluez_device__skeleton_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + g_assert (prop_id != 0 && prop_id - 1 < 19); + g_mutex_lock (&skeleton->priv->lock); + g_object_freeze_notify (object); + if (!_g_value_equal (value, &skeleton->priv->properties[prop_id - 1])) + { + if (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)) != NULL) + _bluez_device__schedule_emit_changed (skeleton, _bluez_device__property_info_pointers[prop_id - 1], prop_id, &skeleton->priv->properties[prop_id - 1]); + g_value_copy (value, &skeleton->priv->properties[prop_id - 1]); + g_object_notify_by_pspec (object, pspec); + } + g_mutex_unlock (&skeleton->priv->lock); + g_object_thaw_notify (object); +} + +static void +bluez_device__skeleton_init (bluezdeviceSkeleton *skeleton) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + skeleton->priv = bluez_device__skeleton_get_instance_private (skeleton); +#else + skeleton->priv = G_TYPE_INSTANCE_GET_PRIVATE (skeleton, BLUEZ_DEVICE_TYPE__SKELETON, bluezdeviceSkeletonPrivate); +#endif + + g_mutex_init (&skeleton->priv->lock); + skeleton->priv->context = g_main_context_ref_thread_default (); + skeleton->priv->properties = g_new0 (GValue, 19); + g_value_init (&skeleton->priv->properties[0], G_TYPE_STRING); + g_value_init (&skeleton->priv->properties[1], G_TYPE_STRING); + g_value_init (&skeleton->priv->properties[2], G_TYPE_STRING); + g_value_init (&skeleton->priv->properties[3], G_TYPE_UINT); + g_value_init (&skeleton->priv->properties[4], G_TYPE_UINT); + g_value_init (&skeleton->priv->properties[5], G_TYPE_STRING); + g_value_init (&skeleton->priv->properties[6], G_TYPE_BOOLEAN); + g_value_init (&skeleton->priv->properties[7], G_TYPE_BOOLEAN); + g_value_init (&skeleton->priv->properties[8], G_TYPE_BOOLEAN); + g_value_init (&skeleton->priv->properties[9], G_TYPE_BOOLEAN); + g_value_init (&skeleton->priv->properties[10], G_TYPE_INT); + g_value_init (&skeleton->priv->properties[11], G_TYPE_BOOLEAN); + g_value_init (&skeleton->priv->properties[12], G_TYPE_STRV); + g_value_init (&skeleton->priv->properties[13], G_TYPE_STRING); + g_value_init (&skeleton->priv->properties[14], G_TYPE_STRING); + g_value_init (&skeleton->priv->properties[15], G_TYPE_VARIANT); + g_value_init (&skeleton->priv->properties[16], G_TYPE_VARIANT); + g_value_init (&skeleton->priv->properties[17], G_TYPE_INT); + g_value_init (&skeleton->priv->properties[18], G_TYPE_STRV); +} + +static const gchar * +bluez_device__skeleton_get_address (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + const gchar *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_string (&(skeleton->priv->properties[0])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar * +bluez_device__skeleton_get_name (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + const gchar *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_string (&(skeleton->priv->properties[1])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar * +bluez_device__skeleton_get_alias (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + const gchar *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_string (&(skeleton->priv->properties[2])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static guint +bluez_device__skeleton_get_class (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + guint value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_uint (&(skeleton->priv->properties[3])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static guint16 +bluez_device__skeleton_get_appearance (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + guint value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_uint (&(skeleton->priv->properties[4])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar * +bluez_device__skeleton_get_icon (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + const gchar *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_string (&(skeleton->priv->properties[5])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static gboolean +bluez_device__skeleton_get_paired (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + gboolean value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_boolean (&(skeleton->priv->properties[6])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static gboolean +bluez_device__skeleton_get_trusted (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + gboolean value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_boolean (&(skeleton->priv->properties[7])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static gboolean +bluez_device__skeleton_get_blocked (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + gboolean value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_boolean (&(skeleton->priv->properties[8])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static gboolean +bluez_device__skeleton_get_legacy_pairing (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + gboolean value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_boolean (&(skeleton->priv->properties[9])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static gint16 +bluez_device__skeleton_get_rssi (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + gint value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_int (&(skeleton->priv->properties[10])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static gboolean +bluez_device__skeleton_get_connected (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + gboolean value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_boolean (&(skeleton->priv->properties[11])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar *const * +bluez_device__skeleton_get_uuids (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + const gchar *const *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_boxed (&(skeleton->priv->properties[12])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar * +bluez_device__skeleton_get_modalias (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + const gchar *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_string (&(skeleton->priv->properties[13])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar * +bluez_device__skeleton_get_adapter (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + const gchar *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_string (&(skeleton->priv->properties[14])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static GVariant * +bluez_device__skeleton_get_manufacturer_data (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + GVariant *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_variant (&(skeleton->priv->properties[15])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static GVariant * +bluez_device__skeleton_get_service_data (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + GVariant *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_variant (&(skeleton->priv->properties[16])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static gint16 +bluez_device__skeleton_get_tx_power (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + gint value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_int (&(skeleton->priv->properties[17])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar *const * +bluez_device__skeleton_get_gatt_services (bluezdevice *object) +{ + bluezdeviceSkeleton *skeleton = BLUEZ_DEVICE__SKELETON (object); + const gchar *const *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_boxed (&(skeleton->priv->properties[18])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static void +bluez_device__skeleton_class_init (bluezdeviceSkeletonClass *klass) +{ + GObjectClass *gobject_class; + GDBusInterfaceSkeletonClass *skeleton_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_device__skeleton_finalize; + gobject_class->get_property = bluez_device__skeleton_get_property; + gobject_class->set_property = bluez_device__skeleton_set_property; + gobject_class->notify = bluez_device__skeleton_notify; + + + bluez_device__override_properties (gobject_class, 1); + + skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass); + skeleton_class->get_info = bluez_device__skeleton_dbus_interface_get_info; + skeleton_class->get_properties = bluez_device__skeleton_dbus_interface_get_properties; + skeleton_class->flush = bluez_device__skeleton_dbus_interface_flush; + skeleton_class->get_vtable = bluez_device__skeleton_dbus_interface_get_vtable; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezdeviceSkeletonPrivate)); +#endif +} + +static void +bluez_device__skeleton_iface_init (bluezdeviceIface *iface) +{ + iface->get_address = bluez_device__skeleton_get_address; + iface->get_name = bluez_device__skeleton_get_name; + iface->get_alias = bluez_device__skeleton_get_alias; + iface->get_class = bluez_device__skeleton_get_class; + iface->get_appearance = bluez_device__skeleton_get_appearance; + iface->get_icon = bluez_device__skeleton_get_icon; + iface->get_paired = bluez_device__skeleton_get_paired; + iface->get_trusted = bluez_device__skeleton_get_trusted; + iface->get_blocked = bluez_device__skeleton_get_blocked; + iface->get_legacy_pairing = bluez_device__skeleton_get_legacy_pairing; + iface->get_rssi = bluez_device__skeleton_get_rssi; + iface->get_connected = bluez_device__skeleton_get_connected; + iface->get_uuids = bluez_device__skeleton_get_uuids; + iface->get_modalias = bluez_device__skeleton_get_modalias; + iface->get_adapter = bluez_device__skeleton_get_adapter; + iface->get_manufacturer_data = bluez_device__skeleton_get_manufacturer_data; + iface->get_service_data = bluez_device__skeleton_get_service_data; + iface->get_tx_power = bluez_device__skeleton_get_tx_power; + iface->get_gatt_services = bluez_device__skeleton_get_gatt_services; +} + +/** + * bluez_device__skeleton_new: + * + * Creates a skeleton object for the D-Bus interface org.bluez.Device1. + * + * Returns: (transfer full) (type bluezdeviceSkeleton): The skeleton object. + */ +bluezdevice * +bluez_device__skeleton_new (void) +{ + return BLUEZ_DEVICE_ (g_object_new (BLUEZ_DEVICE_TYPE__SKELETON, NULL)); +} + +/* ------------------------------------------------------------------------ + * Code for interface org.freedesktop.DBus.Properties + * ------------------------------------------------------------------------ + */ + +/** + * SECTION:bluezdeviceOrgFreedesktopDBusProperties + * @title: bluezdeviceOrgFreedesktopDBusProperties + * @short_description: Generated C code for the org.freedesktop.DBus.Properties D-Bus interface + * + * This section contains code for working with the org.freedesktop.DBus.Properties D-Bus interface in C. + */ + +/* ---- Introspection data for org.freedesktop.DBus.Properties ---- */ + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_properties_method_info_get_IN_ARG_interface = +{ + { + -1, + (gchar *) "interface", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_properties_method_info_get_IN_ARG_name = +{ + { + -1, + (gchar *) "name", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_device_org_freedesktop_dbus_properties_method_info_get_IN_ARG_pointers[] = +{ + &_bluez_device_org_freedesktop_dbus_properties_method_info_get_IN_ARG_interface, + &_bluez_device_org_freedesktop_dbus_properties_method_info_get_IN_ARG_name, + NULL +}; + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_properties_method_info_get_OUT_ARG_value = +{ + { + -1, + (gchar *) "value", + (gchar *) "v", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_device_org_freedesktop_dbus_properties_method_info_get_OUT_ARG_pointers[] = +{ + &_bluez_device_org_freedesktop_dbus_properties_method_info_get_OUT_ARG_value, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_device_org_freedesktop_dbus_properties_method_info_get = +{ + { + -1, + (gchar *) "Get", + (GDBusArgInfo **) &_bluez_device_org_freedesktop_dbus_properties_method_info_get_IN_ARG_pointers, + (GDBusArgInfo **) &_bluez_device_org_freedesktop_dbus_properties_method_info_get_OUT_ARG_pointers, + NULL + }, + "handle-get", + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_properties_method_info_set_IN_ARG_interface = +{ + { + -1, + (gchar *) "interface", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_properties_method_info_set_IN_ARG_name = +{ + { + -1, + (gchar *) "name", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_properties_method_info_set_IN_ARG_value = +{ + { + -1, + (gchar *) "value", + (gchar *) "v", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_device_org_freedesktop_dbus_properties_method_info_set_IN_ARG_pointers[] = +{ + &_bluez_device_org_freedesktop_dbus_properties_method_info_set_IN_ARG_interface, + &_bluez_device_org_freedesktop_dbus_properties_method_info_set_IN_ARG_name, + &_bluez_device_org_freedesktop_dbus_properties_method_info_set_IN_ARG_value, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_device_org_freedesktop_dbus_properties_method_info_set = +{ + { + -1, + (gchar *) "Set", + (GDBusArgInfo **) &_bluez_device_org_freedesktop_dbus_properties_method_info_set_IN_ARG_pointers, + NULL, + NULL + }, + "handle-set", + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_properties_method_info_get_all_IN_ARG_interface = +{ + { + -1, + (gchar *) "interface", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_device_org_freedesktop_dbus_properties_method_info_get_all_IN_ARG_pointers[] = +{ + &_bluez_device_org_freedesktop_dbus_properties_method_info_get_all_IN_ARG_interface, + NULL +}; + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_properties_method_info_get_all_OUT_ARG_properties = +{ + { + -1, + (gchar *) "properties", + (gchar *) "a{sv}", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_device_org_freedesktop_dbus_properties_method_info_get_all_OUT_ARG_pointers[] = +{ + &_bluez_device_org_freedesktop_dbus_properties_method_info_get_all_OUT_ARG_properties, + NULL +}; + +static const _ExtendedGDBusMethodInfo _bluez_device_org_freedesktop_dbus_properties_method_info_get_all = +{ + { + -1, + (gchar *) "GetAll", + (GDBusArgInfo **) &_bluez_device_org_freedesktop_dbus_properties_method_info_get_all_IN_ARG_pointers, + (GDBusArgInfo **) &_bluez_device_org_freedesktop_dbus_properties_method_info_get_all_OUT_ARG_pointers, + NULL + }, + "handle-get-all", + FALSE +}; + +static const _ExtendedGDBusMethodInfo * const _bluez_device_org_freedesktop_dbus_properties_method_info_pointers[] = +{ + &_bluez_device_org_freedesktop_dbus_properties_method_info_get, + &_bluez_device_org_freedesktop_dbus_properties_method_info_set, + &_bluez_device_org_freedesktop_dbus_properties_method_info_get_all, + NULL +}; + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_interface = +{ + { + -1, + (gchar *) "interface", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_changed_properties = +{ + { + -1, + (gchar *) "changed_properties", + (gchar *) "a{sv}", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo _bluez_device_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_invalidated_properties = +{ + { + -1, + (gchar *) "invalidated_properties", + (gchar *) "as", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo * const _bluez_device_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_pointers[] = +{ + &_bluez_device_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_interface, + &_bluez_device_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_changed_properties, + &_bluez_device_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_invalidated_properties, + NULL +}; + +static const _ExtendedGDBusSignalInfo _bluez_device_org_freedesktop_dbus_properties_signal_info_properties_changed = +{ + { + -1, + (gchar *) "PropertiesChanged", + (GDBusArgInfo **) &_bluez_device_org_freedesktop_dbus_properties_signal_info_properties_changed_ARG_pointers, + NULL + }, + "properties-changed" +}; + +static const _ExtendedGDBusSignalInfo * const _bluez_device_org_freedesktop_dbus_properties_signal_info_pointers[] = +{ + &_bluez_device_org_freedesktop_dbus_properties_signal_info_properties_changed, + NULL +}; + +static const _ExtendedGDBusInterfaceInfo _bluez_device_org_freedesktop_dbus_properties_interface_info = +{ + { + -1, + (gchar *) "org.freedesktop.DBus.Properties", + (GDBusMethodInfo **) &_bluez_device_org_freedesktop_dbus_properties_method_info_pointers, + (GDBusSignalInfo **) &_bluez_device_org_freedesktop_dbus_properties_signal_info_pointers, + NULL, + NULL + }, + "org-freedesktop-dbus-properties", +}; + + +/** + * bluez_device_org_freedesktop_dbus_properties_interface_info: + * + * Gets a machine-readable description of the org.freedesktop.DBus.Properties D-Bus interface. + * + * Returns: (transfer none): A #GDBusInterfaceInfo. Do not free. + */ +GDBusInterfaceInfo * +bluez_device_org_freedesktop_dbus_properties_interface_info (void) +{ + return (GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_properties_interface_info.parent_struct; +} + +/** + * bluez_device_org_freedesktop_dbus_properties_override_properties: + * @klass: The class structure for a #GObject-derived class. + * @property_id_begin: The property id to assign to the first overridden property. + * + * Overrides all #GObject properties in the #bluezdeviceOrgFreedesktopDBusProperties interface for a concrete class. + * The properties are overridden in the order they are defined. + * + * Returns: The last property id. + */ +guint +bluez_device_org_freedesktop_dbus_properties_override_properties (GObjectClass *klass, guint property_id_begin) +{ + return property_id_begin - 1; +} + + + +/** + * bluezdeviceOrgFreedesktopDBusProperties: + * + * Abstract interface type for the D-Bus interface org.freedesktop.DBus.Properties. + */ + +/** + * bluezdeviceOrgFreedesktopDBusPropertiesIface: + * @parent_iface: The parent interface. + * @handle_get: Handler for the #bluezdeviceOrgFreedesktopDBusProperties::handle-get signal. + * @handle_get_all: Handler for the #bluezdeviceOrgFreedesktopDBusProperties::handle-get-all signal. + * @handle_set: Handler for the #bluezdeviceOrgFreedesktopDBusProperties::handle-set signal. + * @properties_changed: Handler for the #bluezdeviceOrgFreedesktopDBusProperties::properties-changed signal. + * + * Virtual table for the D-Bus interface org.freedesktop.DBus.Properties. + */ + +typedef bluezdeviceOrgFreedesktopDBusPropertiesIface bluezdeviceOrgFreedesktopDBusPropertiesInterface; +G_DEFINE_INTERFACE (bluezdeviceOrgFreedesktopDBusProperties, bluez_device_org_freedesktop_dbus_properties, G_TYPE_OBJECT); + +static void +bluez_device_org_freedesktop_dbus_properties_default_init (bluezdeviceOrgFreedesktopDBusPropertiesIface *iface) +{ + /* GObject signals for incoming D-Bus method calls: */ + /** + * bluezdeviceOrgFreedesktopDBusProperties::handle-get: + * @object: A #bluezdeviceOrgFreedesktopDBusProperties. + * @invocation: A #GDBusMethodInvocation. + * @arg_interface: Argument passed by remote caller. + * @arg_name: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the Get() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_device_org_freedesktop_dbus_properties_complete_get() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-get", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezdeviceOrgFreedesktopDBusPropertiesIface, handle_get), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 3, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING, G_TYPE_STRING); + + /** + * bluezdeviceOrgFreedesktopDBusProperties::handle-set: + * @object: A #bluezdeviceOrgFreedesktopDBusProperties. + * @invocation: A #GDBusMethodInvocation. + * @arg_interface: Argument passed by remote caller. + * @arg_name: Argument passed by remote caller. + * @arg_value: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the Set() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_device_org_freedesktop_dbus_properties_complete_set() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-set", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezdeviceOrgFreedesktopDBusPropertiesIface, handle_set), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 4, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_VARIANT); + + /** + * bluezdeviceOrgFreedesktopDBusProperties::handle-get-all: + * @object: A #bluezdeviceOrgFreedesktopDBusProperties. + * @invocation: A #GDBusMethodInvocation. + * @arg_interface: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the GetAll() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call bluez_device_org_freedesktop_dbus_properties_complete_get_all() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-get-all", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezdeviceOrgFreedesktopDBusPropertiesIface, handle_get_all), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 2, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING); + + /* GObject signals for received D-Bus signals: */ + /** + * bluezdeviceOrgFreedesktopDBusProperties::properties-changed: + * @object: A #bluezdeviceOrgFreedesktopDBusProperties. + * @arg_interface: Argument. + * @arg_changed_properties: Argument. + * @arg_invalidated_properties: Argument. + * + * On the client-side, this signal is emitted whenever the D-Bus signal "PropertiesChanged" is received. + * + * On the service-side, this signal can be used with e.g. g_signal_emit_by_name() to make the object emit the D-Bus signal. + */ + g_signal_new ("properties-changed", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (bluezdeviceOrgFreedesktopDBusPropertiesIface, properties_changed), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 3, G_TYPE_STRING, G_TYPE_VARIANT, G_TYPE_STRV); + +} + +/** + * bluez_device_org_freedesktop_dbus_properties_emit_properties_changed: + * @object: A #bluezdeviceOrgFreedesktopDBusProperties. + * @arg_interface: Argument to pass with the signal. + * @arg_changed_properties: Argument to pass with the signal. + * @arg_invalidated_properties: Argument to pass with the signal. + * + * Emits the "PropertiesChanged" D-Bus signal. + */ +void +bluez_device_org_freedesktop_dbus_properties_emit_properties_changed ( + bluezdeviceOrgFreedesktopDBusProperties *object, + const gchar *arg_interface, + GVariant *arg_changed_properties, + const gchar *const *arg_invalidated_properties) +{ + g_signal_emit_by_name (object, "properties-changed", arg_interface, arg_changed_properties, arg_invalidated_properties); +} + +/** + * bluez_device_org_freedesktop_dbus_properties_call_get: + * @proxy: A #bluezdeviceOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @arg_name: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the Get() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device_org_freedesktop_dbus_properties_call_get_finish() to get the result of the operation. + * + * See bluez_device_org_freedesktop_dbus_properties_call_get_sync() for the synchronous, blocking version of this method. + */ +void +bluez_device_org_freedesktop_dbus_properties_call_get ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "Get", + g_variant_new ("(ss)", + arg_interface, + arg_name), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_device_org_freedesktop_dbus_properties_call_get_finish: + * @proxy: A #bluezdeviceOrgFreedesktopDBusPropertiesProxy. + * @out_value: (out): Return location for return parameter or %NULL to ignore. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device_org_freedesktop_dbus_properties_call_get(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_device_org_freedesktop_dbus_properties_call_get(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device_org_freedesktop_dbus_properties_call_get_finish ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + GVariant **out_value, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(@v)", + out_value); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_properties_call_get_sync: + * @proxy: A #bluezdeviceOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @arg_name: Argument to pass with the method invocation. + * @out_value: (out): Return location for return parameter or %NULL to ignore. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the Get() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_device_org_freedesktop_dbus_properties_call_get() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device_org_freedesktop_dbus_properties_call_get_sync ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant **out_value, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "Get", + g_variant_new ("(ss)", + arg_interface, + arg_name), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(@v)", + out_value); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_properties_call_set: + * @proxy: A #bluezdeviceOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @arg_name: Argument to pass with the method invocation. + * @arg_value: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the Set() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device_org_freedesktop_dbus_properties_call_set_finish() to get the result of the operation. + * + * See bluez_device_org_freedesktop_dbus_properties_call_set_sync() for the synchronous, blocking version of this method. + */ +void +bluez_device_org_freedesktop_dbus_properties_call_set ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant *arg_value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "Set", + g_variant_new ("(ss@v)", + arg_interface, + arg_name, + arg_value), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_device_org_freedesktop_dbus_properties_call_set_finish: + * @proxy: A #bluezdeviceOrgFreedesktopDBusPropertiesProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device_org_freedesktop_dbus_properties_call_set(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_device_org_freedesktop_dbus_properties_call_set(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device_org_freedesktop_dbus_properties_call_set_finish ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_properties_call_set_sync: + * @proxy: A #bluezdeviceOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @arg_name: Argument to pass with the method invocation. + * @arg_value: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the Set() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_device_org_freedesktop_dbus_properties_call_set() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device_org_freedesktop_dbus_properties_call_set_sync ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + const gchar *arg_name, + GVariant *arg_value, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "Set", + g_variant_new ("(ss@v)", + arg_interface, + arg_name, + arg_value), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_properties_call_get_all: + * @proxy: A #bluezdeviceOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the GetAll() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device_org_freedesktop_dbus_properties_call_get_all_finish() to get the result of the operation. + * + * See bluez_device_org_freedesktop_dbus_properties_call_get_all_sync() for the synchronous, blocking version of this method. + */ +void +bluez_device_org_freedesktop_dbus_properties_call_get_all ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "GetAll", + g_variant_new ("(s)", + arg_interface), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * bluez_device_org_freedesktop_dbus_properties_call_get_all_finish: + * @proxy: A #bluezdeviceOrgFreedesktopDBusPropertiesProxy. + * @out_properties: (out): Return location for return parameter or %NULL to ignore. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device_org_freedesktop_dbus_properties_call_get_all(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with bluez_device_org_freedesktop_dbus_properties_call_get_all(). + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device_org_freedesktop_dbus_properties_call_get_all_finish ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + GVariant **out_properties, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(@a{sv})", + out_properties); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_properties_call_get_all_sync: + * @proxy: A #bluezdeviceOrgFreedesktopDBusPropertiesProxy. + * @arg_interface: Argument to pass with the method invocation. + * @out_properties: (out): Return location for return parameter or %NULL to ignore. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the GetAll() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See bluez_device_org_freedesktop_dbus_properties_call_get_all() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeded, %FALSE if @error is set. + */ +gboolean +bluez_device_org_freedesktop_dbus_properties_call_get_all_sync ( + bluezdeviceOrgFreedesktopDBusProperties *proxy, + const gchar *arg_interface, + GVariant **out_properties, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "GetAll", + g_variant_new ("(s)", + arg_interface), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(@a{sv})", + out_properties); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_properties_complete_get: + * @object: A #bluezdeviceOrgFreedesktopDBusProperties. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * @value: Parameter to return. + * + * Helper function used in service implementations to finish handling invocations of the Get() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_device_org_freedesktop_dbus_properties_complete_get ( + bluezdeviceOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + GVariant *value) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(@v)", + value)); +} + +/** + * bluez_device_org_freedesktop_dbus_properties_complete_set: + * @object: A #bluezdeviceOrgFreedesktopDBusProperties. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the Set() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_device_org_freedesktop_dbus_properties_complete_set ( + bluezdeviceOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/** + * bluez_device_org_freedesktop_dbus_properties_complete_get_all: + * @object: A #bluezdeviceOrgFreedesktopDBusProperties. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * @properties: Parameter to return. + * + * Helper function used in service implementations to finish handling invocations of the GetAll() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +bluez_device_org_freedesktop_dbus_properties_complete_get_all ( + bluezdeviceOrgFreedesktopDBusProperties *object, + GDBusMethodInvocation *invocation, + GVariant *properties) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(@a{sv})", + properties)); +} + +/* ------------------------------------------------------------------------ */ + +/** + * bluezdeviceOrgFreedesktopDBusPropertiesProxy: + * + * The #bluezdeviceOrgFreedesktopDBusPropertiesProxy structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezdeviceOrgFreedesktopDBusPropertiesProxyClass: + * @parent_class: The parent class. + * + * Class structure for #bluezdeviceOrgFreedesktopDBusPropertiesProxy. + */ + +struct _bluezdeviceOrgFreedesktopDBusPropertiesProxyPrivate +{ + GData *qdata; +}; + +static void bluez_device_org_freedesktop_dbus_properties_proxy_iface_init (bluezdeviceOrgFreedesktopDBusPropertiesIface *iface); + +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgFreedesktopDBusPropertiesProxy, bluez_device_org_freedesktop_dbus_properties_proxy, G_TYPE_DBUS_PROXY, + G_ADD_PRIVATE (bluezdeviceOrgFreedesktopDBusPropertiesProxy) + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluez_device_org_freedesktop_dbus_properties_proxy_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgFreedesktopDBusPropertiesProxy, bluez_device_org_freedesktop_dbus_properties_proxy, G_TYPE_DBUS_PROXY, + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluez_device_org_freedesktop_dbus_properties_proxy_iface_init)); + +#endif +static void +bluez_device_org_freedesktop_dbus_properties_proxy_finalize (GObject *object) +{ + bluezdeviceOrgFreedesktopDBusPropertiesProxy *proxy = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY (object); + g_datalist_clear (&proxy->priv->qdata); + G_OBJECT_CLASS (bluez_device_org_freedesktop_dbus_properties_proxy_parent_class)->finalize (object); +} + +static void +bluez_device_org_freedesktop_dbus_properties_proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ +} + +static void +bluez_device_org_freedesktop_dbus_properties_proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ +} + +static void +bluez_device_org_freedesktop_dbus_properties_proxy_g_signal (GDBusProxy *proxy, + const gchar *sender_name G_GNUC_UNUSED, + const gchar *signal_name, + GVariant *parameters) +{ + _ExtendedGDBusSignalInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint n; + guint signal_id; + info = (_ExtendedGDBusSignalInfo *) g_dbus_interface_info_lookup_signal ((GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_properties_interface_info.parent_struct, signal_name); + if (info == NULL) + return; + num_params = g_variant_n_children (parameters); + paramv = g_new0 (GValue, num_params + 1); + g_value_init (¶mv[0], BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES); + g_value_set_object (¶mv[0], proxy); + g_variant_iter_init (&iter, parameters); + n = 1; + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.args[n - 1]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES); + g_signal_emitv (paramv, signal_id, 0, NULL); + for (n = 0; n < num_params + 1; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static void +bluez_device_org_freedesktop_dbus_properties_proxy_g_properties_changed (GDBusProxy *_proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties) +{ + bluezdeviceOrgFreedesktopDBusPropertiesProxy *proxy = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY (_proxy); + guint n; + const gchar *key; + GVariantIter *iter; + _ExtendedGDBusPropertyInfo *info; + g_variant_get (changed_properties, "a{sv}", &iter); + while (g_variant_iter_next (iter, "{&sv}", &key, NULL)) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_properties_interface_info.parent_struct, key); + g_datalist_remove_data (&proxy->priv->qdata, key); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } + g_variant_iter_free (iter); + for (n = 0; invalidated_properties[n] != NULL; n++) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_properties_interface_info.parent_struct, invalidated_properties[n]); + g_datalist_remove_data (&proxy->priv->qdata, invalidated_properties[n]); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } +} + +static void +bluez_device_org_freedesktop_dbus_properties_proxy_init (bluezdeviceOrgFreedesktopDBusPropertiesProxy *proxy) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + proxy->priv = bluez_device_org_freedesktop_dbus_properties_proxy_get_instance_private (proxy); +#else + proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, bluezdeviceOrgFreedesktopDBusPropertiesProxyPrivate); +#endif + + g_dbus_proxy_set_interface_info (G_DBUS_PROXY (proxy), bluez_device_org_freedesktop_dbus_properties_interface_info ()); +} + +static void +bluez_device_org_freedesktop_dbus_properties_proxy_class_init (bluezdeviceOrgFreedesktopDBusPropertiesProxyClass *klass) +{ + GObjectClass *gobject_class; + GDBusProxyClass *proxy_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_device_org_freedesktop_dbus_properties_proxy_finalize; + gobject_class->get_property = bluez_device_org_freedesktop_dbus_properties_proxy_get_property; + gobject_class->set_property = bluez_device_org_freedesktop_dbus_properties_proxy_set_property; + + proxy_class = G_DBUS_PROXY_CLASS (klass); + proxy_class->g_signal = bluez_device_org_freedesktop_dbus_properties_proxy_g_signal; + proxy_class->g_properties_changed = bluez_device_org_freedesktop_dbus_properties_proxy_g_properties_changed; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezdeviceOrgFreedesktopDBusPropertiesProxyPrivate)); +#endif +} + +static void +bluez_device_org_freedesktop_dbus_properties_proxy_iface_init (bluezdeviceOrgFreedesktopDBusPropertiesIface *iface) +{ +} + +/** + * bluez_device_org_freedesktop_dbus_properties_proxy_new: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Asynchronously creates a proxy for the D-Bus interface org.freedesktop.DBus.Properties. See g_dbus_proxy_new() for more details. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device_org_freedesktop_dbus_properties_proxy_new_finish() to get the result of the operation. + * + * See bluez_device_org_freedesktop_dbus_properties_proxy_new_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_device_org_freedesktop_dbus_properties_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Properties", NULL); +} + +/** + * bluez_device_org_freedesktop_dbus_properties_proxy_new_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device_org_freedesktop_dbus_properties_proxy_new(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_device_org_freedesktop_dbus_properties_proxy_new(). + * + * Returns: (transfer full) (type bluezdeviceOrgFreedesktopDBusPropertiesProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgFreedesktopDBusProperties * +bluez_device_org_freedesktop_dbus_properties_proxy_new_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES (ret); + else + return NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_properties_proxy_new_sync: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Synchronously creates a proxy for the D-Bus interface org.freedesktop.DBus.Properties. See g_dbus_proxy_new_sync() for more details. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_device_org_freedesktop_dbus_properties_proxy_new() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezdeviceOrgFreedesktopDBusPropertiesProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgFreedesktopDBusProperties * +bluez_device_org_freedesktop_dbus_properties_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Properties", NULL); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES (ret); + else + return NULL; +} + + +/** + * bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Like bluez_device_org_freedesktop_dbus_properties_proxy_new() but takes a #GBusType instead of a #GDBusConnection. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus_finish() to get the result of the operation. + * + * See bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Properties", NULL); +} + +/** + * bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus(). + * + * Returns: (transfer full) (type bluezdeviceOrgFreedesktopDBusPropertiesProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgFreedesktopDBusProperties * +bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES (ret); + else + return NULL; +} + +/** + * bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus_sync: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Like bluez_device_org_freedesktop_dbus_properties_proxy_new_sync() but takes a #GBusType instead of a #GDBusConnection. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezdeviceOrgFreedesktopDBusPropertiesProxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgFreedesktopDBusProperties * +bluez_device_org_freedesktop_dbus_properties_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.freedesktop.DBus.Properties", NULL); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES (ret); + else + return NULL; +} + + +/* ------------------------------------------------------------------------ */ + +/** + * bluezdeviceOrgFreedesktopDBusPropertiesSkeleton: + * + * The #bluezdeviceOrgFreedesktopDBusPropertiesSkeleton structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezdeviceOrgFreedesktopDBusPropertiesSkeletonClass: + * @parent_class: The parent class. + * + * Class structure for #bluezdeviceOrgFreedesktopDBusPropertiesSkeleton. + */ + +struct _bluezdeviceOrgFreedesktopDBusPropertiesSkeletonPrivate +{ + GValue *properties; + GList *changed_properties; + GSource *changed_properties_idle_source; + GMainContext *context; + GMutex lock; +}; + +static void +_bluez_device_org_freedesktop_dbus_properties_skeleton_handle_method_call ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + bluezdeviceOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (user_data); + _ExtendedGDBusMethodInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint num_extra; + guint n; + guint signal_id; + GValue return_value = G_VALUE_INIT; + info = (_ExtendedGDBusMethodInfo *) g_dbus_method_invocation_get_method_info (invocation); + g_assert (info != NULL); + num_params = g_variant_n_children (parameters); + num_extra = info->pass_fdlist ? 3 : 2; paramv = g_new0 (GValue, num_params + num_extra); + n = 0; + g_value_init (¶mv[n], BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES); + g_value_set_object (¶mv[n++], skeleton); + g_value_init (¶mv[n], G_TYPE_DBUS_METHOD_INVOCATION); + g_value_set_object (¶mv[n++], invocation); + if (info->pass_fdlist) + { +#ifdef G_OS_UNIX + g_value_init (¶mv[n], G_TYPE_UNIX_FD_LIST); + g_value_set_object (¶mv[n++], g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation))); +#else + g_assert_not_reached (); +#endif + } + g_variant_iter_init (&iter, parameters); + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.in_args[n - num_extra]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES); + g_value_init (&return_value, G_TYPE_BOOLEAN); + g_signal_emitv (paramv, signal_id, 0, &return_value); + if (!g_value_get_boolean (&return_value)) + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method %s is not implemented on interface %s", method_name, interface_name); + g_value_unset (&return_value); + for (n = 0; n < num_params + num_extra; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static GVariant * +_bluez_device_org_freedesktop_dbus_properties_skeleton_handle_get_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + bluezdeviceOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + GVariant *ret; + ret = NULL; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_properties_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + g_value_init (&value, pspec->value_type); + g_object_get_property (G_OBJECT (skeleton), info->hyphen_name, &value); + ret = g_dbus_gvalue_to_gvariant (&value, G_VARIANT_TYPE (info->parent_struct.signature)); + g_value_unset (&value); + } + return ret; +} + +static gboolean +_bluez_device_org_freedesktop_dbus_properties_skeleton_handle_set_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GVariant *variant, + GError **error, + gpointer user_data) +{ + bluezdeviceOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + gboolean ret; + ret = FALSE; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_freedesktop_dbus_properties_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + if (info->use_gvariant) + g_value_set_variant (&value, variant); + else + g_dbus_gvariant_to_gvalue (variant, &value); + g_object_set_property (G_OBJECT (skeleton), info->hyphen_name, &value); + g_value_unset (&value); + ret = TRUE; + } + return ret; +} + +static const GDBusInterfaceVTable _bluez_device_org_freedesktop_dbus_properties_skeleton_vtable = +{ + _bluez_device_org_freedesktop_dbus_properties_skeleton_handle_method_call, + _bluez_device_org_freedesktop_dbus_properties_skeleton_handle_get_property, + _bluez_device_org_freedesktop_dbus_properties_skeleton_handle_set_property, + {NULL} +}; + +static GDBusInterfaceInfo * +bluez_device_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_info (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return bluez_device_org_freedesktop_dbus_properties_interface_info (); +} + +static GDBusInterfaceVTable * +bluez_device_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_vtable (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return (GDBusInterfaceVTable *) &_bluez_device_org_freedesktop_dbus_properties_skeleton_vtable; +} + +static GVariant * +bluez_device_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_properties (GDBusInterfaceSkeleton *_skeleton) +{ + bluezdeviceOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (_skeleton); + + GVariantBuilder builder; + guint n; + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + if (_bluez_device_org_freedesktop_dbus_properties_interface_info.parent_struct.properties == NULL) + goto out; + for (n = 0; _bluez_device_org_freedesktop_dbus_properties_interface_info.parent_struct.properties[n] != NULL; n++) + { + GDBusPropertyInfo *info = _bluez_device_org_freedesktop_dbus_properties_interface_info.parent_struct.properties[n]; + if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) + { + GVariant *value; + value = _bluez_device_org_freedesktop_dbus_properties_skeleton_handle_get_property (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)), NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.freedesktop.DBus.Properties", info->name, NULL, skeleton); + if (value != NULL) + { + g_variant_take_ref (value); + g_variant_builder_add (&builder, "{sv}", info->name, value); + g_variant_unref (value); + } + } + } +out: + return g_variant_builder_end (&builder); +} + +static void +bluez_device_org_freedesktop_dbus_properties_skeleton_dbus_interface_flush (GDBusInterfaceSkeleton *_skeleton) +{ +} + +static void +_bluez_device_org_freedesktop_dbus_properties_on_signal_properties_changed ( + bluezdeviceOrgFreedesktopDBusProperties *object, + const gchar *arg_interface, + GVariant *arg_changed_properties, + const gchar *const *arg_invalidated_properties) +{ + bluezdeviceOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (object); + + GList *connections, *l; + GVariant *signal_variant; + connections = g_dbus_interface_skeleton_get_connections (G_DBUS_INTERFACE_SKELETON (skeleton)); + + signal_variant = g_variant_ref_sink (g_variant_new ("(s@a{sv}^as)", + arg_interface, + arg_changed_properties, + arg_invalidated_properties)); + for (l = connections; l != NULL; l = l->next) + { + GDBusConnection *connection = l->data; + g_dbus_connection_emit_signal (connection, + NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.freedesktop.DBus.Properties", "PropertiesChanged", + signal_variant, NULL); + } + g_variant_unref (signal_variant); + g_list_free_full (connections, g_object_unref); +} + +static void bluez_device_org_freedesktop_dbus_properties_skeleton_iface_init (bluezdeviceOrgFreedesktopDBusPropertiesIface *iface); +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgFreedesktopDBusPropertiesSkeleton, bluez_device_org_freedesktop_dbus_properties_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_ADD_PRIVATE (bluezdeviceOrgFreedesktopDBusPropertiesSkeleton) + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluez_device_org_freedesktop_dbus_properties_skeleton_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgFreedesktopDBusPropertiesSkeleton, bluez_device_org_freedesktop_dbus_properties_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES, bluez_device_org_freedesktop_dbus_properties_skeleton_iface_init)); + +#endif +static void +bluez_device_org_freedesktop_dbus_properties_skeleton_finalize (GObject *object) +{ + bluezdeviceOrgFreedesktopDBusPropertiesSkeleton *skeleton = BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON (object); + g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free); + if (skeleton->priv->changed_properties_idle_source != NULL) + g_source_destroy (skeleton->priv->changed_properties_idle_source); + g_main_context_unref (skeleton->priv->context); + g_mutex_clear (&skeleton->priv->lock); + G_OBJECT_CLASS (bluez_device_org_freedesktop_dbus_properties_skeleton_parent_class)->finalize (object); +} + +static void +bluez_device_org_freedesktop_dbus_properties_skeleton_init (bluezdeviceOrgFreedesktopDBusPropertiesSkeleton *skeleton) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + skeleton->priv = bluez_device_org_freedesktop_dbus_properties_skeleton_get_instance_private (skeleton); +#else + skeleton->priv = G_TYPE_INSTANCE_GET_PRIVATE (skeleton, BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON, bluezdeviceOrgFreedesktopDBusPropertiesSkeletonPrivate); +#endif + + g_mutex_init (&skeleton->priv->lock); + skeleton->priv->context = g_main_context_ref_thread_default (); +} + +static void +bluez_device_org_freedesktop_dbus_properties_skeleton_class_init (bluezdeviceOrgFreedesktopDBusPropertiesSkeletonClass *klass) +{ + GObjectClass *gobject_class; + GDBusInterfaceSkeletonClass *skeleton_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_device_org_freedesktop_dbus_properties_skeleton_finalize; + + skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass); + skeleton_class->get_info = bluez_device_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_info; + skeleton_class->get_properties = bluez_device_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_properties; + skeleton_class->flush = bluez_device_org_freedesktop_dbus_properties_skeleton_dbus_interface_flush; + skeleton_class->get_vtable = bluez_device_org_freedesktop_dbus_properties_skeleton_dbus_interface_get_vtable; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezdeviceOrgFreedesktopDBusPropertiesSkeletonPrivate)); +#endif +} + +static void +bluez_device_org_freedesktop_dbus_properties_skeleton_iface_init (bluezdeviceOrgFreedesktopDBusPropertiesIface *iface) +{ + iface->properties_changed = _bluez_device_org_freedesktop_dbus_properties_on_signal_properties_changed; +} + +/** + * bluez_device_org_freedesktop_dbus_properties_skeleton_new: + * + * Creates a skeleton object for the D-Bus interface org.freedesktop.DBus.Properties. + * + * Returns: (transfer full) (type bluezdeviceOrgFreedesktopDBusPropertiesSkeleton): The skeleton object. + */ +bluezdeviceOrgFreedesktopDBusProperties * +bluez_device_org_freedesktop_dbus_properties_skeleton_new (void) +{ + return BLUEZ_DEVICE_ORG_FREEDESKTOP_DBUS_PROPERTIES (g_object_new (BLUEZ_DEVICE_TYPE_ORG_FREEDESKTOP_DBUS_PROPERTIES_SKELETON, NULL)); +} + +/* ------------------------------------------------------------------------ + * Code for interface org.bluez.ProximityReporter1 + * ------------------------------------------------------------------------ + */ + +/** + * SECTION:bluezdeviceOrgBluezProximityReporter1 + * @title: bluezdeviceOrgBluezProximityReporter1 + * @short_description: Generated C code for the org.bluez.ProximityReporter1 D-Bus interface + * + * This section contains code for working with the org.bluez.ProximityReporter1 D-Bus interface in C. + */ + +/* ---- Introspection data for org.bluez.ProximityReporter1 ---- */ + +static const _ExtendedGDBusPropertyInfo _bluez_device_org_bluez_proximity_reporter1_property_info_link_loss_alert_level = +{ + { + -1, + (gchar *) "LinkLossAlertLevel", + (gchar *) "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "link-loss-alert-level", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo _bluez_device_org_bluez_proximity_reporter1_property_info_immediate_alert_level = +{ + { + -1, + (gchar *) "ImmediateAlertLevel", + (gchar *) "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + "immediate-alert-level", + FALSE +}; + +static const _ExtendedGDBusPropertyInfo * const _bluez_device_org_bluez_proximity_reporter1_property_info_pointers[] = +{ + &_bluez_device_org_bluez_proximity_reporter1_property_info_link_loss_alert_level, + &_bluez_device_org_bluez_proximity_reporter1_property_info_immediate_alert_level, + NULL +}; + +static const _ExtendedGDBusInterfaceInfo _bluez_device_org_bluez_proximity_reporter1_interface_info = +{ + { + -1, + (gchar *) "org.bluez.ProximityReporter1", + NULL, + NULL, + (GDBusPropertyInfo **) &_bluez_device_org_bluez_proximity_reporter1_property_info_pointers, + NULL + }, + "org-bluez-proximity-reporter1", +}; + + +/** + * bluez_device_org_bluez_proximity_reporter1_interface_info: + * + * Gets a machine-readable description of the org.bluez.ProximityReporter1 D-Bus interface. + * + * Returns: (transfer none): A #GDBusInterfaceInfo. Do not free. + */ +GDBusInterfaceInfo * +bluez_device_org_bluez_proximity_reporter1_interface_info (void) +{ + return (GDBusInterfaceInfo *) &_bluez_device_org_bluez_proximity_reporter1_interface_info.parent_struct; +} + +/** + * bluez_device_org_bluez_proximity_reporter1_override_properties: + * @klass: The class structure for a #GObject-derived class. + * @property_id_begin: The property id to assign to the first overridden property. + * + * Overrides all #GObject properties in the #bluezdeviceOrgBluezProximityReporter1 interface for a concrete class. + * The properties are overridden in the order they are defined. + * + * Returns: The last property id. + */ +guint +bluez_device_org_bluez_proximity_reporter1_override_properties (GObjectClass *klass, guint property_id_begin) +{ + g_object_class_override_property (klass, property_id_begin++, "link-loss-alert-level"); + g_object_class_override_property (klass, property_id_begin++, "immediate-alert-level"); + return property_id_begin - 1; +} + + + +/** + * bluezdeviceOrgBluezProximityReporter1: + * + * Abstract interface type for the D-Bus interface org.bluez.ProximityReporter1. + */ + +/** + * bluezdeviceOrgBluezProximityReporter1Iface: + * @parent_iface: The parent interface. + * @get_immediate_alert_level: Getter for the #bluezdeviceOrgBluezProximityReporter1:immediate-alert-level property. + * @get_link_loss_alert_level: Getter for the #bluezdeviceOrgBluezProximityReporter1:link-loss-alert-level property. + * + * Virtual table for the D-Bus interface org.bluez.ProximityReporter1. + */ + +typedef bluezdeviceOrgBluezProximityReporter1Iface bluezdeviceOrgBluezProximityReporter1Interface; +G_DEFINE_INTERFACE (bluezdeviceOrgBluezProximityReporter1, bluez_device_org_bluez_proximity_reporter1, G_TYPE_OBJECT); + +static void +bluez_device_org_bluez_proximity_reporter1_default_init (bluezdeviceOrgBluezProximityReporter1Iface *iface) +{ + /* GObject properties for D-Bus properties: */ + /** + * bluezdeviceOrgBluezProximityReporter1:link-loss-alert-level: + * + * Represents the D-Bus property "LinkLossAlertLevel". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("link-loss-alert-level", "LinkLossAlertLevel", "LinkLossAlertLevel", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * bluezdeviceOrgBluezProximityReporter1:immediate-alert-level: + * + * Represents the D-Bus property "ImmediateAlertLevel". + * + * Since the D-Bus property for this #GObject property is readable but not writable, it is meaningful to read from it on both the client- and service-side. It is only meaningful, however, to write to it on the service-side. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("immediate-alert-level", "ImmediateAlertLevel", "ImmediateAlertLevel", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +/** + * bluez_device_org_bluez_proximity_reporter1_get_link_loss_alert_level: (skip) + * @object: A #bluezdeviceOrgBluezProximityReporter1. + * + * Gets the value of the "LinkLossAlertLevel" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device_org_bluez_proximity_reporter1_dup_link_loss_alert_level() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar * +bluez_device_org_bluez_proximity_reporter1_get_link_loss_alert_level (bluezdeviceOrgBluezProximityReporter1 *object) +{ + return BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_GET_IFACE (object)->get_link_loss_alert_level (object); +} + +/** + * bluez_device_org_bluez_proximity_reporter1_dup_link_loss_alert_level: (skip) + * @object: A #bluezdeviceOrgBluezProximityReporter1. + * + * Gets a copy of the "LinkLossAlertLevel" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_free(). + */ +gchar * +bluez_device_org_bluez_proximity_reporter1_dup_link_loss_alert_level (bluezdeviceOrgBluezProximityReporter1 *object) +{ + gchar *value; + g_object_get (G_OBJECT (object), "link-loss-alert-level", &value, NULL); + return value; +} + +/** + * bluez_device_org_bluez_proximity_reporter1_set_link_loss_alert_level: (skip) + * @object: A #bluezdeviceOrgBluezProximityReporter1. + * @value: The value to set. + * + * Sets the "LinkLossAlertLevel" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device_org_bluez_proximity_reporter1_set_link_loss_alert_level (bluezdeviceOrgBluezProximityReporter1 *object, const gchar *value) +{ + g_object_set (G_OBJECT (object), "link-loss-alert-level", value, NULL); +} + +/** + * bluez_device_org_bluez_proximity_reporter1_get_immediate_alert_level: (skip) + * @object: A #bluezdeviceOrgBluezProximityReporter1. + * + * Gets the value of the "ImmediateAlertLevel" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use bluez_device_org_bluez_proximity_reporter1_dup_immediate_alert_level() if on another thread. + * + * Returns: (transfer none): The property value or %NULL if the property is not set. Do not free the returned value, it belongs to @object. + */ +const gchar * +bluez_device_org_bluez_proximity_reporter1_get_immediate_alert_level (bluezdeviceOrgBluezProximityReporter1 *object) +{ + return BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_GET_IFACE (object)->get_immediate_alert_level (object); +} + +/** + * bluez_device_org_bluez_proximity_reporter1_dup_immediate_alert_level: (skip) + * @object: A #bluezdeviceOrgBluezProximityReporter1. + * + * Gets a copy of the "ImmediateAlertLevel" D-Bus property. + * + * Since this D-Bus property is readable, it is meaningful to use this function on both the client- and service-side. + * + * Returns: (transfer full): The property value or %NULL if the property is not set. The returned value should be freed with g_free(). + */ +gchar * +bluez_device_org_bluez_proximity_reporter1_dup_immediate_alert_level (bluezdeviceOrgBluezProximityReporter1 *object) +{ + gchar *value; + g_object_get (G_OBJECT (object), "immediate-alert-level", &value, NULL); + return value; +} + +/** + * bluez_device_org_bluez_proximity_reporter1_set_immediate_alert_level: (skip) + * @object: A #bluezdeviceOrgBluezProximityReporter1. + * @value: The value to set. + * + * Sets the "ImmediateAlertLevel" D-Bus property to @value. + * + * Since this D-Bus property is not writable, it is only meaningful to use this function on the service-side. + */ +void +bluez_device_org_bluez_proximity_reporter1_set_immediate_alert_level (bluezdeviceOrgBluezProximityReporter1 *object, const gchar *value) +{ + g_object_set (G_OBJECT (object), "immediate-alert-level", value, NULL); +} + +/* ------------------------------------------------------------------------ */ + +/** + * bluezdeviceOrgBluezProximityReporter1Proxy: + * + * The #bluezdeviceOrgBluezProximityReporter1Proxy structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezdeviceOrgBluezProximityReporter1ProxyClass: + * @parent_class: The parent class. + * + * Class structure for #bluezdeviceOrgBluezProximityReporter1Proxy. + */ + +struct _bluezdeviceOrgBluezProximityReporter1ProxyPrivate +{ + GData *qdata; +}; + +static void bluez_device_org_bluez_proximity_reporter1_proxy_iface_init (bluezdeviceOrgBluezProximityReporter1Iface *iface); + +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgBluezProximityReporter1Proxy, bluez_device_org_bluez_proximity_reporter1_proxy, G_TYPE_DBUS_PROXY, + G_ADD_PRIVATE (bluezdeviceOrgBluezProximityReporter1Proxy) + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1, bluez_device_org_bluez_proximity_reporter1_proxy_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgBluezProximityReporter1Proxy, bluez_device_org_bluez_proximity_reporter1_proxy, G_TYPE_DBUS_PROXY, + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1, bluez_device_org_bluez_proximity_reporter1_proxy_iface_init)); + +#endif +static void +bluez_device_org_bluez_proximity_reporter1_proxy_finalize (GObject *object) +{ + bluezdeviceOrgBluezProximityReporter1Proxy *proxy = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY (object); + g_datalist_clear (&proxy->priv->qdata); + G_OBJECT_CLASS (bluez_device_org_bluez_proximity_reporter1_proxy_parent_class)->finalize (object); +} + +static void +bluez_device_org_bluez_proximity_reporter1_proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ + const _ExtendedGDBusPropertyInfo *info; + GVariant *variant; + g_assert (prop_id != 0 && prop_id - 1 < 2); + info = _bluez_device_org_bluez_proximity_reporter1_property_info_pointers[prop_id - 1]; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (object), info->parent_struct.name); + if (info->use_gvariant) + { + g_value_set_variant (value, variant); + } + else + { + if (variant != NULL) + g_dbus_gvariant_to_gvalue (variant, value); + } + if (variant != NULL) + g_variant_unref (variant); +} + +static void +bluez_device_org_bluez_proximity_reporter1_proxy_set_property_cb (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + const _ExtendedGDBusPropertyInfo *info = user_data; + GError *error; + GVariant *_ret; + error = NULL; + _ret = g_dbus_proxy_call_finish (proxy, res, &error); + if (!_ret) + { + g_warning ("Error setting property '%s' on interface org.bluez.ProximityReporter1: %s (%s, %d)", + info->parent_struct.name, + error->message, g_quark_to_string (error->domain), error->code); + g_error_free (error); + } + else + { + g_variant_unref (_ret); + } +} + +static void +bluez_device_org_bluez_proximity_reporter1_proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ + const _ExtendedGDBusPropertyInfo *info; + GVariant *variant; + g_assert (prop_id != 0 && prop_id - 1 < 2); + info = _bluez_device_org_bluez_proximity_reporter1_property_info_pointers[prop_id - 1]; + variant = g_dbus_gvalue_to_gvariant (value, G_VARIANT_TYPE (info->parent_struct.signature)); + g_dbus_proxy_call (G_DBUS_PROXY (object), + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", "org.bluez.ProximityReporter1", info->parent_struct.name, variant), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, (GAsyncReadyCallback) bluez_device_org_bluez_proximity_reporter1_proxy_set_property_cb, (GDBusPropertyInfo *) &info->parent_struct); + g_variant_unref (variant); +} + +static void +bluez_device_org_bluez_proximity_reporter1_proxy_g_signal (GDBusProxy *proxy, + const gchar *sender_name G_GNUC_UNUSED, + const gchar *signal_name, + GVariant *parameters) +{ + _ExtendedGDBusSignalInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint n; + guint signal_id; + info = (_ExtendedGDBusSignalInfo *) g_dbus_interface_info_lookup_signal ((GDBusInterfaceInfo *) &_bluez_device_org_bluez_proximity_reporter1_interface_info.parent_struct, signal_name); + if (info == NULL) + return; + num_params = g_variant_n_children (parameters); + paramv = g_new0 (GValue, num_params + 1); + g_value_init (¶mv[0], BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1); + g_value_set_object (¶mv[0], proxy); + g_variant_iter_init (&iter, parameters); + n = 1; + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.args[n - 1]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1); + g_signal_emitv (paramv, signal_id, 0, NULL); + for (n = 0; n < num_params + 1; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static void +bluez_device_org_bluez_proximity_reporter1_proxy_g_properties_changed (GDBusProxy *_proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties) +{ + bluezdeviceOrgBluezProximityReporter1Proxy *proxy = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY (_proxy); + guint n; + const gchar *key; + GVariantIter *iter; + _ExtendedGDBusPropertyInfo *info; + g_variant_get (changed_properties, "a{sv}", &iter); + while (g_variant_iter_next (iter, "{&sv}", &key, NULL)) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_bluez_proximity_reporter1_interface_info.parent_struct, key); + g_datalist_remove_data (&proxy->priv->qdata, key); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } + g_variant_iter_free (iter); + for (n = 0; invalidated_properties[n] != NULL; n++) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_bluez_proximity_reporter1_interface_info.parent_struct, invalidated_properties[n]); + g_datalist_remove_data (&proxy->priv->qdata, invalidated_properties[n]); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } +} + +static const gchar * +bluez_device_org_bluez_proximity_reporter1_proxy_get_link_loss_alert_level (bluezdeviceOrgBluezProximityReporter1 *object) +{ + bluezdeviceOrgBluezProximityReporter1Proxy *proxy = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY (object); + GVariant *variant; + const gchar *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "LinkLossAlertLevel"); + if (variant != NULL) + { + value = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static const gchar * +bluez_device_org_bluez_proximity_reporter1_proxy_get_immediate_alert_level (bluezdeviceOrgBluezProximityReporter1 *object) +{ + bluezdeviceOrgBluezProximityReporter1Proxy *proxy = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY (object); + GVariant *variant; + const gchar *value = NULL; + variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "ImmediateAlertLevel"); + if (variant != NULL) + { + value = g_variant_get_string (variant, NULL); + g_variant_unref (variant); + } + return value; +} + +static void +bluez_device_org_bluez_proximity_reporter1_proxy_init (bluezdeviceOrgBluezProximityReporter1Proxy *proxy) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + proxy->priv = bluez_device_org_bluez_proximity_reporter1_proxy_get_instance_private (proxy); +#else + proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY, bluezdeviceOrgBluezProximityReporter1ProxyPrivate); +#endif + + g_dbus_proxy_set_interface_info (G_DBUS_PROXY (proxy), bluez_device_org_bluez_proximity_reporter1_interface_info ()); +} + +static void +bluez_device_org_bluez_proximity_reporter1_proxy_class_init (bluezdeviceOrgBluezProximityReporter1ProxyClass *klass) +{ + GObjectClass *gobject_class; + GDBusProxyClass *proxy_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_device_org_bluez_proximity_reporter1_proxy_finalize; + gobject_class->get_property = bluez_device_org_bluez_proximity_reporter1_proxy_get_property; + gobject_class->set_property = bluez_device_org_bluez_proximity_reporter1_proxy_set_property; + + proxy_class = G_DBUS_PROXY_CLASS (klass); + proxy_class->g_signal = bluez_device_org_bluez_proximity_reporter1_proxy_g_signal; + proxy_class->g_properties_changed = bluez_device_org_bluez_proximity_reporter1_proxy_g_properties_changed; + + bluez_device_org_bluez_proximity_reporter1_override_properties (gobject_class, 1); + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezdeviceOrgBluezProximityReporter1ProxyPrivate)); +#endif +} + +static void +bluez_device_org_bluez_proximity_reporter1_proxy_iface_init (bluezdeviceOrgBluezProximityReporter1Iface *iface) +{ + iface->get_link_loss_alert_level = bluez_device_org_bluez_proximity_reporter1_proxy_get_link_loss_alert_level; + iface->get_immediate_alert_level = bluez_device_org_bluez_proximity_reporter1_proxy_get_immediate_alert_level; +} + +/** + * bluez_device_org_bluez_proximity_reporter1_proxy_new: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Asynchronously creates a proxy for the D-Bus interface org.bluez.ProximityReporter1. See g_dbus_proxy_new() for more details. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device_org_bluez_proximity_reporter1_proxy_new_finish() to get the result of the operation. + * + * See bluez_device_org_bluez_proximity_reporter1_proxy_new_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_device_org_bluez_proximity_reporter1_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.bluez.ProximityReporter1", NULL); +} + +/** + * bluez_device_org_bluez_proximity_reporter1_proxy_new_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device_org_bluez_proximity_reporter1_proxy_new(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_device_org_bluez_proximity_reporter1_proxy_new(). + * + * Returns: (transfer full) (type bluezdeviceOrgBluezProximityReporter1Proxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgBluezProximityReporter1 * +bluez_device_org_bluez_proximity_reporter1_proxy_new_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1 (ret); + else + return NULL; +} + +/** + * bluez_device_org_bluez_proximity_reporter1_proxy_new_sync: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (allow-none): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Synchronously creates a proxy for the D-Bus interface org.bluez.ProximityReporter1. See g_dbus_proxy_new_sync() for more details. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_device_org_bluez_proximity_reporter1_proxy_new() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezdeviceOrgBluezProximityReporter1Proxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgBluezProximityReporter1 * +bluez_device_org_bluez_proximity_reporter1_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.bluez.ProximityReporter1", NULL); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1 (ret); + else + return NULL; +} + + +/** + * bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Like bluez_device_org_bluez_proximity_reporter1_proxy_new() but takes a #GBusType instead of a #GDBusConnection. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from. + * You can then call bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus_finish() to get the result of the operation. + * + * See bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus_sync() for the synchronous, blocking version of this constructor. + */ +void +bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.bluez.ProximityReporter1", NULL); +} + +/** + * bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus(). + * + * Returns: (transfer full) (type bluezdeviceOrgBluezProximityReporter1Proxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgBluezProximityReporter1 * +bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1 (ret); + else + return NULL; +} + +/** + * bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus_sync: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Like bluez_device_org_bluez_proximity_reporter1_proxy_new_sync() but takes a #GBusType instead of a #GDBusConnection. + * + * The calling thread is blocked until a reply is received. + * + * See bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type bluezdeviceOrgBluezProximityReporter1Proxy): The constructed proxy object or %NULL if @error is set. + */ +bluezdeviceOrgBluezProximityReporter1 * +bluez_device_org_bluez_proximity_reporter1_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.bluez.ProximityReporter1", NULL); + if (ret != NULL) + return BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1 (ret); + else + return NULL; +} + + +/* ------------------------------------------------------------------------ */ + +/** + * bluezdeviceOrgBluezProximityReporter1Skeleton: + * + * The #bluezdeviceOrgBluezProximityReporter1Skeleton structure contains only private data and should only be accessed using the provided API. + */ + +/** + * bluezdeviceOrgBluezProximityReporter1SkeletonClass: + * @parent_class: The parent class. + * + * Class structure for #bluezdeviceOrgBluezProximityReporter1Skeleton. + */ + +struct _bluezdeviceOrgBluezProximityReporter1SkeletonPrivate +{ + GValue *properties; + GList *changed_properties; + GSource *changed_properties_idle_source; + GMainContext *context; + GMutex lock; +}; + +static void +_bluez_device_org_bluez_proximity_reporter1_skeleton_handle_method_call ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (user_data); + _ExtendedGDBusMethodInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + guint num_params; + guint num_extra; + guint n; + guint signal_id; + GValue return_value = G_VALUE_INIT; + info = (_ExtendedGDBusMethodInfo *) g_dbus_method_invocation_get_method_info (invocation); + g_assert (info != NULL); + num_params = g_variant_n_children (parameters); + num_extra = info->pass_fdlist ? 3 : 2; paramv = g_new0 (GValue, num_params + num_extra); + n = 0; + g_value_init (¶mv[n], BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1); + g_value_set_object (¶mv[n++], skeleton); + g_value_init (¶mv[n], G_TYPE_DBUS_METHOD_INVOCATION); + g_value_set_object (¶mv[n++], invocation); + if (info->pass_fdlist) + { +#ifdef G_OS_UNIX + g_value_init (¶mv[n], G_TYPE_UNIX_FD_LIST); + g_value_set_object (¶mv[n++], g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation))); +#else + g_assert_not_reached (); +#endif + } + g_variant_iter_init (&iter, parameters); + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.in_args[n - num_extra]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1); + g_value_init (&return_value, G_TYPE_BOOLEAN); + g_signal_emitv (paramv, signal_id, 0, &return_value); + if (!g_value_get_boolean (&return_value)) + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method %s is not implemented on interface %s", method_name, interface_name); + g_value_unset (&return_value); + for (n = 0; n < num_params + num_extra; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static GVariant * +_bluez_device_org_bluez_proximity_reporter1_skeleton_handle_get_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + GVariant *ret; + ret = NULL; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_bluez_proximity_reporter1_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + g_value_init (&value, pspec->value_type); + g_object_get_property (G_OBJECT (skeleton), info->hyphen_name, &value); + ret = g_dbus_gvalue_to_gvariant (&value, G_VARIANT_TYPE (info->parent_struct.signature)); + g_value_unset (&value); + } + return ret; +} + +static gboolean +_bluez_device_org_bluez_proximity_reporter1_skeleton_handle_set_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GVariant *variant, + GError **error, + gpointer user_data) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + gboolean ret; + ret = FALSE; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_bluez_device_org_bluez_proximity_reporter1_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + if (info->use_gvariant) + g_value_set_variant (&value, variant); + else + g_dbus_gvariant_to_gvalue (variant, &value); + g_object_set_property (G_OBJECT (skeleton), info->hyphen_name, &value); + g_value_unset (&value); + ret = TRUE; + } + return ret; +} + +static const GDBusInterfaceVTable _bluez_device_org_bluez_proximity_reporter1_skeleton_vtable = +{ + _bluez_device_org_bluez_proximity_reporter1_skeleton_handle_method_call, + _bluez_device_org_bluez_proximity_reporter1_skeleton_handle_get_property, + _bluez_device_org_bluez_proximity_reporter1_skeleton_handle_set_property, + {NULL} +}; + +static GDBusInterfaceInfo * +bluez_device_org_bluez_proximity_reporter1_skeleton_dbus_interface_get_info (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return bluez_device_org_bluez_proximity_reporter1_interface_info (); +} + +static GDBusInterfaceVTable * +bluez_device_org_bluez_proximity_reporter1_skeleton_dbus_interface_get_vtable (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return (GDBusInterfaceVTable *) &_bluez_device_org_bluez_proximity_reporter1_skeleton_vtable; +} + +static GVariant * +bluez_device_org_bluez_proximity_reporter1_skeleton_dbus_interface_get_properties (GDBusInterfaceSkeleton *_skeleton) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (_skeleton); + + GVariantBuilder builder; + guint n; + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + if (_bluez_device_org_bluez_proximity_reporter1_interface_info.parent_struct.properties == NULL) + goto out; + for (n = 0; _bluez_device_org_bluez_proximity_reporter1_interface_info.parent_struct.properties[n] != NULL; n++) + { + GDBusPropertyInfo *info = _bluez_device_org_bluez_proximity_reporter1_interface_info.parent_struct.properties[n]; + if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) + { + GVariant *value; + value = _bluez_device_org_bluez_proximity_reporter1_skeleton_handle_get_property (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)), NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.bluez.ProximityReporter1", info->name, NULL, skeleton); + if (value != NULL) + { + g_variant_take_ref (value); + g_variant_builder_add (&builder, "{sv}", info->name, value); + g_variant_unref (value); + } + } + } +out: + return g_variant_builder_end (&builder); +} + +static gboolean _bluez_device_org_bluez_proximity_reporter1_emit_changed (gpointer user_data); + +static void +bluez_device_org_bluez_proximity_reporter1_skeleton_dbus_interface_flush (GDBusInterfaceSkeleton *_skeleton) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (_skeleton); + gboolean emit_changed = FALSE; + + g_mutex_lock (&skeleton->priv->lock); + if (skeleton->priv->changed_properties_idle_source != NULL) + { + g_source_destroy (skeleton->priv->changed_properties_idle_source); + skeleton->priv->changed_properties_idle_source = NULL; + emit_changed = TRUE; + } + g_mutex_unlock (&skeleton->priv->lock); + + if (emit_changed) + _bluez_device_org_bluez_proximity_reporter1_emit_changed (skeleton); +} + +static void bluez_device_org_bluez_proximity_reporter1_skeleton_iface_init (bluezdeviceOrgBluezProximityReporter1Iface *iface); +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgBluezProximityReporter1Skeleton, bluez_device_org_bluez_proximity_reporter1_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_ADD_PRIVATE (bluezdeviceOrgBluezProximityReporter1Skeleton) + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1, bluez_device_org_bluez_proximity_reporter1_skeleton_iface_init)); + +#else +G_DEFINE_TYPE_WITH_CODE (bluezdeviceOrgBluezProximityReporter1Skeleton, bluez_device_org_bluez_proximity_reporter1_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_IMPLEMENT_INTERFACE (BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1, bluez_device_org_bluez_proximity_reporter1_skeleton_iface_init)); + +#endif +static void +bluez_device_org_bluez_proximity_reporter1_skeleton_finalize (GObject *object) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (object); + guint n; + for (n = 0; n < 2; n++) + g_value_unset (&skeleton->priv->properties[n]); + g_free (skeleton->priv->properties); + g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free); + if (skeleton->priv->changed_properties_idle_source != NULL) + g_source_destroy (skeleton->priv->changed_properties_idle_source); + g_main_context_unref (skeleton->priv->context); + g_mutex_clear (&skeleton->priv->lock); + G_OBJECT_CLASS (bluez_device_org_bluez_proximity_reporter1_skeleton_parent_class)->finalize (object); +} + +static void +bluez_device_org_bluez_proximity_reporter1_skeleton_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (object); + g_assert (prop_id != 0 && prop_id - 1 < 2); + g_mutex_lock (&skeleton->priv->lock); + g_value_copy (&skeleton->priv->properties[prop_id - 1], value); + g_mutex_unlock (&skeleton->priv->lock); +} + +static gboolean +_bluez_device_org_bluez_proximity_reporter1_emit_changed (gpointer user_data) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (user_data); + GList *l; + GVariantBuilder builder; + GVariantBuilder invalidated_builder; + guint num_changes; + + g_mutex_lock (&skeleton->priv->lock); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as")); + for (l = skeleton->priv->changed_properties, num_changes = 0; l != NULL; l = l->next) + { + ChangedProperty *cp = l->data; + GVariant *variant; + const GValue *cur_value; + + cur_value = &skeleton->priv->properties[cp->prop_id - 1]; + if (!_g_value_equal (cur_value, &cp->orig_value)) + { + variant = g_dbus_gvalue_to_gvariant (cur_value, G_VARIANT_TYPE (cp->info->parent_struct.signature)); + g_variant_builder_add (&builder, "{sv}", cp->info->parent_struct.name, variant); + g_variant_unref (variant); + num_changes++; + } + } + if (num_changes > 0) + { + GList *connections, *ll; + GVariant *signal_variant; + signal_variant = g_variant_ref_sink (g_variant_new ("(sa{sv}as)", "org.bluez.ProximityReporter1", + &builder, &invalidated_builder)); + connections = g_dbus_interface_skeleton_get_connections (G_DBUS_INTERFACE_SKELETON (skeleton)); + for (ll = connections; ll != NULL; ll = ll->next) + { + GDBusConnection *connection = ll->data; + + g_dbus_connection_emit_signal (connection, + NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + signal_variant, + NULL); + } + g_variant_unref (signal_variant); + g_list_free_full (connections, g_object_unref); + } + else + { + g_variant_builder_clear (&builder); + g_variant_builder_clear (&invalidated_builder); + } + g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free); + skeleton->priv->changed_properties = NULL; + skeleton->priv->changed_properties_idle_source = NULL; + g_mutex_unlock (&skeleton->priv->lock); + return FALSE; +} + +static void +_bluez_device_org_bluez_proximity_reporter1_schedule_emit_changed (bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton, const _ExtendedGDBusPropertyInfo *info, guint prop_id, const GValue *orig_value) +{ + ChangedProperty *cp; + GList *l; + cp = NULL; + for (l = skeleton->priv->changed_properties; l != NULL; l = l->next) + { + ChangedProperty *i_cp = l->data; + if (i_cp->info == info) + { + cp = i_cp; + break; + } + } + if (cp == NULL) + { + cp = g_new0 (ChangedProperty, 1); + cp->prop_id = prop_id; + cp->info = info; + skeleton->priv->changed_properties = g_list_prepend (skeleton->priv->changed_properties, cp); + g_value_init (&cp->orig_value, G_VALUE_TYPE (orig_value)); + g_value_copy (orig_value, &cp->orig_value); + } +} + +static void +bluez_device_org_bluez_proximity_reporter1_skeleton_notify (GObject *object, + GParamSpec *pspec G_GNUC_UNUSED) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (object); + g_mutex_lock (&skeleton->priv->lock); + if (skeleton->priv->changed_properties != NULL && + skeleton->priv->changed_properties_idle_source == NULL) + { + skeleton->priv->changed_properties_idle_source = g_idle_source_new (); + g_source_set_priority (skeleton->priv->changed_properties_idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (skeleton->priv->changed_properties_idle_source, _bluez_device_org_bluez_proximity_reporter1_emit_changed, g_object_ref (skeleton), (GDestroyNotify) g_object_unref); + g_source_attach (skeleton->priv->changed_properties_idle_source, skeleton->priv->context); + g_source_unref (skeleton->priv->changed_properties_idle_source); + } + g_mutex_unlock (&skeleton->priv->lock); +} + +static void +bluez_device_org_bluez_proximity_reporter1_skeleton_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (object); + g_assert (prop_id != 0 && prop_id - 1 < 2); + g_mutex_lock (&skeleton->priv->lock); + g_object_freeze_notify (object); + if (!_g_value_equal (value, &skeleton->priv->properties[prop_id - 1])) + { + if (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)) != NULL) + _bluez_device_org_bluez_proximity_reporter1_schedule_emit_changed (skeleton, _bluez_device_org_bluez_proximity_reporter1_property_info_pointers[prop_id - 1], prop_id, &skeleton->priv->properties[prop_id - 1]); + g_value_copy (value, &skeleton->priv->properties[prop_id - 1]); + g_object_notify_by_pspec (object, pspec); + } + g_mutex_unlock (&skeleton->priv->lock); + g_object_thaw_notify (object); +} + +static void +bluez_device_org_bluez_proximity_reporter1_skeleton_init (bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + skeleton->priv = bluez_device_org_bluez_proximity_reporter1_skeleton_get_instance_private (skeleton); +#else + skeleton->priv = G_TYPE_INSTANCE_GET_PRIVATE (skeleton, BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON, bluezdeviceOrgBluezProximityReporter1SkeletonPrivate); +#endif + + g_mutex_init (&skeleton->priv->lock); + skeleton->priv->context = g_main_context_ref_thread_default (); + skeleton->priv->properties = g_new0 (GValue, 2); + g_value_init (&skeleton->priv->properties[0], G_TYPE_STRING); + g_value_init (&skeleton->priv->properties[1], G_TYPE_STRING); +} + +static const gchar * +bluez_device_org_bluez_proximity_reporter1_skeleton_get_link_loss_alert_level (bluezdeviceOrgBluezProximityReporter1 *object) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (object); + const gchar *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_string (&(skeleton->priv->properties[0])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static const gchar * +bluez_device_org_bluez_proximity_reporter1_skeleton_get_immediate_alert_level (bluezdeviceOrgBluezProximityReporter1 *object) +{ + bluezdeviceOrgBluezProximityReporter1Skeleton *skeleton = BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON (object); + const gchar *value; + g_mutex_lock (&skeleton->priv->lock); + value = g_value_get_string (&(skeleton->priv->properties[1])); + g_mutex_unlock (&skeleton->priv->lock); + return value; +} + +static void +bluez_device_org_bluez_proximity_reporter1_skeleton_class_init (bluezdeviceOrgBluezProximityReporter1SkeletonClass *klass) +{ + GObjectClass *gobject_class; + GDBusInterfaceSkeletonClass *skeleton_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = bluez_device_org_bluez_proximity_reporter1_skeleton_finalize; + gobject_class->get_property = bluez_device_org_bluez_proximity_reporter1_skeleton_get_property; + gobject_class->set_property = bluez_device_org_bluez_proximity_reporter1_skeleton_set_property; + gobject_class->notify = bluez_device_org_bluez_proximity_reporter1_skeleton_notify; + + + bluez_device_org_bluez_proximity_reporter1_override_properties (gobject_class, 1); + + skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass); + skeleton_class->get_info = bluez_device_org_bluez_proximity_reporter1_skeleton_dbus_interface_get_info; + skeleton_class->get_properties = bluez_device_org_bluez_proximity_reporter1_skeleton_dbus_interface_get_properties; + skeleton_class->flush = bluez_device_org_bluez_proximity_reporter1_skeleton_dbus_interface_flush; + skeleton_class->get_vtable = bluez_device_org_bluez_proximity_reporter1_skeleton_dbus_interface_get_vtable; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (bluezdeviceOrgBluezProximityReporter1SkeletonPrivate)); +#endif +} + +static void +bluez_device_org_bluez_proximity_reporter1_skeleton_iface_init (bluezdeviceOrgBluezProximityReporter1Iface *iface) +{ + iface->get_link_loss_alert_level = bluez_device_org_bluez_proximity_reporter1_skeleton_get_link_loss_alert_level; + iface->get_immediate_alert_level = bluez_device_org_bluez_proximity_reporter1_skeleton_get_immediate_alert_level; +} + +/** + * bluez_device_org_bluez_proximity_reporter1_skeleton_new: + * + * Creates a skeleton object for the D-Bus interface org.bluez.ProximityReporter1. + * + * Returns: (transfer full) (type bluezdeviceOrgBluezProximityReporter1Skeleton): The skeleton object. + */ +bluezdeviceOrgBluezProximityReporter1 * +bluez_device_org_bluez_proximity_reporter1_skeleton_new (void) +{ + return BLUEZ_DEVICE_ORG_BLUEZ_PROXIMITY_REPORTER1 (g_object_new (BLUEZ_DEVICE_TYPE_ORG_BLUEZ_PROXIMITY_REPORTER1_SKELETON, NULL)); +} + diff --git a/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.Device1.xml b/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.Device1.xml new file mode 100644 index 00000000..2b5ed5bc --- /dev/null +++ b/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.Device1.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.GattCharacteristic1.xml b/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.GattCharacteristic1.xml new file mode 100644 index 00000000..9d541af5 --- /dev/null +++ b/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.GattCharacteristic1.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.GattDescriptor1.xml b/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.GattDescriptor1.xml new file mode 100644 index 00000000..c3ba8947 --- /dev/null +++ b/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.GattDescriptor1.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.GattService1.xml b/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.GattService1.xml new file mode 100644 index 00000000..6dbe9f66 --- /dev/null +++ b/modules/ble/deps/linux/dbus-bluez/xml/org.bluez.GattService1.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/ble/devdoc/ble_gatt_io_requirements.md b/modules/ble/devdoc/ble_gatt_io_requirements.md new file mode 100644 index 00000000..d1e7d82a --- /dev/null +++ b/modules/ble/devdoc/ble_gatt_io_requirements.md @@ -0,0 +1,242 @@ +# Bluetooth Low Energy GATT I/O + +## Overview + +The Bluetooth Low Energy (BLE) Generic Attribute Profile (GATT) input/output (I/O) API is designed to be a platform abstraction layer (PAL) API that abstracts away the low level details of communicating with BLE devices on a given platform. + +## References + +* [Generic Attribute Profile](https://developer.bluetooth.org/TechnologyOverview/Pages/GATT.aspx) + +## Additional data types + +```c +/** +* The 6 byte mac address of the BLE device. +*/ +typedef struct BLE_MAC_ADDRESS_TAG +{ + uint8_t address[6]; +}BLE_MAC_ADDRESS; + +typedef struct BLE_DEVICE_CONFIG_TAG +{ + /** + * MAC address of the BLE device. + */ + BLE_MAC_ADDRESS device_addr; + + /** + * Zero based index of the bluetooth controller to be + * used. + */ + uint8_t ble_controller_index; +}BLE_DEVICE_CONFIG; + +typedef struct BLEIO_GATT_INSTANCE_TAG* BLEIO_GATT_HANDLE; + +#define BLEIO_GATT_RESULT_VALUES \ + BLEIO_GATT_OK, \ + BLEIO_GATT_ERROR + +DEFINE_ENUM(BLEIO_GATT_RESULT, BLEIO_GATT_RESULT_VALUES); + +#define BLEIO_GATT_STATE_VALUES \ + BLEIO_GATT_STATE_DISCONNECTED, \ + BLEIO_GATT_STATE_CONNECTING, \ + BLEIO_GATT_STATE_CONNECTED, \ + BLEIO_GATT_STATE_ERROR + +DEFINE_ENUM(BLEIO_GATT_STATE, BLEIO_GATT_STATE_VALUES); + +#define BLEIO_GATT_CONNECT_RESULT_VALUES \ + BLEIO_GATT_CONNECT_OK, \ + BLEIO_GATT_CONNECT_ERROR + +DEFINE_ENUM(BLEIO_GATT_CONNECT_RESULT, BLEIO_GATT_CONNECT_RESULT_VALUES); + +typedef void(*ON_BLEIO_GATT_CONNECT_COMPLETE)(BLEIO_GATT_HANDLE bleio_gatt_handle, void* context, BLEIO_GATT_CONNECT_RESULT connect_result); +typedef void(*ON_BLEIO_GATT_DISCONNECT_COMPLETE)(BLEIO_GATT_HANDLE bleio_gatt_handle, void* context); +typedef void(*ON_BLEIO_GATT_ATTRIB_READ_COMPLETE)(BLEIO_GATT_HANDLE bleio_gatt_handle, void* context, BLEIO_GATT_RESULT result, const unsigned char* buffer, size_t size); +typedef void(*ON_BLEIO_GATT_ATTRIB_WRITE_COMPLETE)(BLEIO_GATT_HANDLE bleio_gatt_handle, void* context, BLEIO_GATT_RESULT result); + +extern BLEIO_GATT_HANDLE BLEIO_gatt_create( + const BLE_DEVICE_CONFIG* config +); + +extern void BLEIO_gatt_destroy( + BLEIO_GATT_HANDLE bleio_gatt_handle +); + +extern int BLEIO_gatt_connect( + BLEIO_GATT_HANDLE bleio_gatt_handle, + ON_BLEIO_GATT_CONNECT_COMPLETE on_bleio_gatt_connect_complete, + void* callback_context +); + +extern void BLEIO_gatt_disconnect( + BLEIO_GATT_HANDLE bleio_gatt_handle, + ON_BLEIO_GATT_DISCONNECT_COMPLETE on_bleio_gatt_disconnect_complete, + void* callback_context +); + +extern int BLEIO_gatt_read_char_by_uuid( + BLEIO_GATT_HANDLE bleio_gatt_handle, + const char* ble_uuid, + ON_BLEIO_GATT_ATTRIB_READ_COMPLETE on_bleio_gatt_attrib_read_complete, + void* callback_context +); + +extern int BLEIO_gatt_write_char_by_uuid( + BLEIO_GATT_HANDLE bleio_gatt_handle, + const char* ble_uuid, + const unsigned char* buffer, + size_t size, + ON_BLEIO_GATT_ATTRIB_WRITE_COMPLETE on_bleio_gatt_attrib_write_complete, + void* callback_context +); +``` + +## BLEIO_gatt_create +```c +extern BLEIO_GATT_HANDLE BLEIO_gatt_create( + const BLE_DEVICE_CONFIG* config +); +``` + +**SRS_BLEIO_GATT_13_001: [** `BLEIO_gatt_create` shall return a non-`NULL` handle on successful execution. **]** + +**SRS_BLEIO_GATT_13_002: [** `BLEIO_gatt_create` shall return `NULL` when any of the underlying platform calls fail. **]** + +**SRS_BLEIO_GATT_13_003: [** `BLEIO_gatt_create` shall return `NULL` if `config` is `NULL`. **]** + +## BLEIO_gatt_destroy +```c +extern void BLEIO_gatt_destroy( + BLEIO_GATT_HANDLE bleio_gatt_handle +); +``` + +> If there are pending I/O operations in progress when this API is called, the semantics of completion/cancellation of those requests is left to the underlying platform. GLIB's IO channels for instance (which is used internally by *bluez*) allow the caller to specify that pending data that is to be written should be flushed before the channel is shutdown. When the platform provides such capabilities this API should attempt to use them to cancel the pending requests. + +**SRS_BLEIO_GATT_13_004: [** `BLEIO_gatt_destroy` shall free all resources associated with the handle. **]** + +**SRS_BLEIO_GATT_13_005: [** If `bleio_gatt_handle` is `NULL` `BLEIO_gatt_destroy` shall do nothing. **]** + +## BLEIO_gatt_connect +```c +extern int BLEIO_gatt_connect( + BLEIO_GATT_HANDLE bleio_gatt_handle, + ON_BLEIO_GATT_CONNECT_COMPLETE on_bleio_gatt_connect_complete, + void* callback_context +); +``` + +**SRS_BLEIO_GATT_13_007: [** `BLEIO_gatt_connect` shall asynchronously attempt to open a connection with the BLE device. **]** + +**SRS_BLEIO_GATT_13_008: [** On initiating the connect successfully, `BLEIO_gatt_connect` shall return `0` (zero). **]** + +**SRS_BLEIO_GATT_13_009: [** If any of the underlying platform calls fail, `BLEIO_gatt_connect` shall return a non-zero value. **]** + +**SRS_BLEIO_GATT_13_010: [** If `bleio_gatt_handle` or `on_bleio_gatt_connect_complete` are `NULL` then `BLEIO_gatt_connect` shall return a non-zero value. **]** + +**SRS_BLEIO_GATT_13_011: [** When the connect operation to the device has been completed, the callback function pointed at by `on_bleio_gatt_connect_complete` shall be invoked. **]** + +**SRS_BLEIO_GATT_13_012: [** When `on_bleio_gatt_connect_complete` is invoked the value passed in `callback_context` to `BLEIO_gatt_connect` shall be passed along to `on_bleio_gatt_connect_complete`. **]** + +**SRS_BLEIO_GATT_13_013: [** The `connect_result` parameter of the `on_bleio_gatt_connect_complete` callback shall indicate the status of the connect operation. **]** + +**SRS_BLEIO_GATT_13_047: [** If when `BLEIO_gatt_connect` is called, there's another connection request already in progress or if an open connection already exists, then this API shall return a non-zero error code. **]** + +## BLEIO_gatt_disconnect +```c +extern void BLEIO_gatt_disconnect( + BLEIO_GATT_HANDLE bleio_gatt_handle, + ON_BLEIO_GATT_DISCONNECT_COMPLETE on_bleio_gatt_disconnect_complete, + void* callback_context +); +``` + +> If there are pending I/O operations in progress when this API is called, the semantics of completion/cancellation of those requests is left to the underlying platform. GLIB's IO channels for instance (which is used internally by *bluez*) allow the caller to specify that pending data that is to be written should be flushed before the channel is shutdown. When the platform provides such capabilities this API should attempt to use them to cancel the pending requests. + +**SRS_BLEIO_GATT_13_014: [** `BLEIO_gatt_disconnect` shall asynchronously disconnect from the BLE device if an open connection exists. **]** + +**SRS_BLEIO_GATT_13_050: [** When the disconnect operation has been completed, the callback function pointed at by `on_bleio_gatt_disconnect_complete` shall be invoked if it is not `NULL`. **]** + +**SRS_BLEIO_GATT_13_049: [** When `on_bleio_gatt_disconnect_complete` is invoked the value passed in `callback_context` to `BLEIO_gatt_disconnect` shall be passed along to `on_bleio_gatt_disconnect_complete`. **]** + +**SRS_BLEIO_GATT_13_015: [** `BLEIO_gatt_disconnect` shall do nothing if an open connection does not exist when it is called. **]** + +**SRS_BLEIO_GATT_13_016: [** `BLEIO_gatt_disconnect` shall do nothing if `bleio_gatt_handle` is `NULL`. **]** + +**SRS_BLEIO_GATT_13_054: [** `BLEIO_gatt_disconnect` shall do nothing if an underlying platform call fails. **]** + +## BLEIO_gatt_read_char_by_uuid + +```c +extern int BLEIO_gatt_read_char_by_uuid( + BLEIO_GATT_HANDLE bleio_gatt_handle, + const BLE_UUID* ble_uuid, + ON_BLEIO_GATT_ATTRIB_READ_COMPLETE on_bleio_gatt_attrib_read_complete, + void* callback_context +); +``` +**SRS_BLEIO_GATT_13_027: [** `BLEIO_gatt_read_char_by_uuid` shall return a non-zero value if `bleio_gatt_handle` is `NULL`. **]** + +**SRS_BLEIO_GATT_13_028: [** `BLEIO_gatt_read_char_by_uuid` shall return a non-zero value if `on_bleio_gatt_attrib_read_complete` is `NULL`. **]** + +**SRS_BLEIO_GATT_13_051: [** `BLEIO_gatt_read_char_by_uuid` shall return a non-zero value if `ble_uuid` is `NULL`. **]** + +**SRS_BLEIO_GATT_13_052: [** `BLEIO_gatt_read_char_by_uuid` shall return a non-zero value if the object is not in a *connected* state. **]** + +**SRS_BLEIO_GATT_13_029: [** `BLEIO_gatt_read_char_by_uuid` shall return a non-zero value if an underlying platform call fails. **]** + +**SRS_BLEIO_GATT_13_030: [** `BLEIO_gatt_read_char_by_uuid` shall return 0 (zero) if the *read characteristic* operation is successful. **]** + +**SRS_BLEIO_GATT_13_031: [** `BLEIO_gatt_read_char_by_uuid` shall asynchronously initiate a *read characteristic* operation using the specified UUID. **]** + +**SRS_BLEIO_GATT_13_032: [** `BLEIO_gatt_read_char_by_uuid` shall invoke `on_bleio_gatt_attrib_read_complete` when the read operation completes. **]** + +**SRS_BLEIO_GATT_13_033: [** `BLEIO_gatt_read_char_by_uuid` shall pass the value of the `callback_context` parameter to `on_bleio_gatt_attrib_read_complete` as the `context` parameter when it is invoked. **]** + +**SRS_BLEIO_GATT_13_034: [** `BLEIO_gatt_read_char_by_uuid`, when successful, shall supply the data that has been read to the `on_bleio_gatt_attrib_read_complete` callback along with the value `BLEIO_GATT_OK` for the `result` parameter. **]** + +**SRS_BLEIO_GATT_13_035: [** When an error occurs asynchronously, the value `BLEIO_GATT_ERROR` shall be passed for the `result` parameter of the `on_bleio_gatt_attrib_read_complete` callback. **]** + +## BLEIO_gatt_write_char_by_uuid + +```c +extern int BLEIO_gatt_write_char_by_uuid( + BLEIO_GATT_HANDLE bleio_gatt_handle, + const char* ble_uuid, + const unsigned char* buffer, + size_t size, + ON_BLEIO_GATT_ATTRIB_WRITE_COMPLETE on_bleio_gatt_attrib_write_complete, + void* callback_context +); +``` +**SRS_BLEIO_GATT_13_036: [** `BLEIO_gatt_write_char_by_uuid` shall return a non-zero value if `bleio_gatt_handle` is `NULL`. **]** + +**SRS_BLEIO_GATT_13_045: [** `BLEIO_gatt_write_char_by_uuid` shall return a non-zero value if `buffer` is `NULL`. **]** + +**SRS_BLEIO_GATT_13_048: [** `BLEIO_gatt_write_char_by_uuid` shall return a non-zero value if `ble_uuid` is `NULL`. **]** + +**SRS_BLEIO_GATT_13_046: [** `BLEIO_gatt_write_char_by_uuid` shall return a non-zero value if `size` is equal to `0` (zero). **]** + +**SRS_BLEIO_GATT_13_053: [** `BLEIO_gatt_write_char_by_uuid` shall return a non-zero value if an active connection to the device does not exist. **]** + +**SRS_BLEIO_GATT_13_037: [** `BLEIO_gatt_write_char_by_uuid` shall return a non-zero value if `on_bleio_gatt_attrib_write_complete` is `NULL`. **]** + +**SRS_BLEIO_GATT_13_038: [** `BLEIO_gatt_write_char_by_uuid` shall return a non-zero value if an underlying platform call fails. **]** + +**SRS_BLEIO_GATT_13_039: [** `BLEIO_gatt_write_char_by_uuid` shall return 0 (zero) if the *write characteristic* operation is successful. **]** + +**SRS_BLEIO_GATT_13_040: [** `BLEIO_gatt_write_char_by_uuid` shall asynchronously initiate a *write characteristic* operation using the specified UUID. **]** + +**SRS_BLEIO_GATT_13_041: [** `BLEIO_gatt_write_char_by_uuid` shall invoke `on_bleio_gatt_attrib_write_complete` when the write operation completes. **]** + +**SRS_BLEIO_GATT_13_042: [** `BLEIO_gatt_write_char_by_uuid` shall pass the value of the `callback_context` parameter to `on_bleio_gatt_attrib_write_complete` as the `context` parameter when it is invoked. **]** + +**SRS_BLEIO_GATT_13_043: [** `BLEIO_gatt_write_char_by_uuid`, when successful, shall supply the value `BLEIO_GATT_OK` for the `result` parameter. **]** + +**SRS_BLEIO_GATT_13_044: [** When an error occurs asynchronously, the value `BLEIO_GATT_ERROR` shall be passed for the `result` parameter of the `on_bleio_gatt_attrib_write_complete` callback. **]** \ No newline at end of file diff --git a/modules/ble/devdoc/bleio_seq_requirements.md b/modules/ble/devdoc/bleio_seq_requirements.md new file mode 100644 index 00000000..2f74d43e --- /dev/null +++ b/modules/ble/devdoc/bleio_seq_requirements.md @@ -0,0 +1,232 @@ +# Bluetooth Low Energy GATT I/O Request Sequencer + +## Overview + +The Bluetooth Low Energy (BLE) GATT I/O Request Sequencer component is an abstraction that is designed to make the job of executing a sequence of BLE I/O operations simpler. It defines the notion of an *instruction* which represents a read or a write operation that needs to be executed on the BLE device potentially at some well defined interval. + +## References + +* [BLE GATT I/O Requirements](./ble_gatt_io_requirements.md) + +## Data types + +```c +typedef struct BLEIO_SEQ_HANDLE_DATA_TAG* BLEIO_SEQ_HANDLE; + +#define BLEIO_SEQ_RESULT_VALUES \ + BLEIO_SEQ_ERROR, \ + BLEIO_SEQ_OK +DEFINE_ENUM(BLEIO_SEQ_RESULT, BLEIO_SEQ_RESULT_VALUES); + +#define BLEIO_SEQ_INSTRUCTION_TYPE_VALUES \ + READ_ONCE, \ + READ_PERIODIC, \ + WRITE_ONCE, \ + WRITE_AT_INIT, \ + WRITE_AT_EXIT +DEFINE_ENUM(BLEIO_SEQ_INSTRUCTION_TYPE, BLEIO_SEQ_INSTRUCTION_TYPE_VALUES); + +typedef struct BLEIO_SEQ_INSTRUCTION_TAG +{ + /** + * The type of instruction this is from the BLEIO_SEQ_INSTRUCTION_TYPE enum. + */ + BLEIO_SEQ_INSTRUCTION_TYPE instruction_type; + + /** + * The GATT characteristic to read from/write to. + */ + const char* characteristic_uuid; + + /** + * Context data that should be passed back to the callback that is invoked + * when this instruction completes execution (or for every instance of + * completion in case this is a recurring instruction). + */ + void* context; + + union + { + /** + * If 'instruction_type' is equal to READ_PERIODIC then this + * value indicates the polling interval in milliseconds. + */ + uint32_t interval_in_ms; + + /** + * If 'instruction_type' is equal to WRITE_AT_INIT or WRITE_AT_EXIT + * or WRITE_ONCE then this is the buffer that is to be written. + */ + BUFFER_HANDLE buffer; + }data; +}BLEIO_SEQ_INSTRUCTION; + +/** + * Callback invoked when the sequencer completes a read operation. + */ +typedef void(*ON_BLEIO_SEQ_READ_COMPLETE)( + BLEIO_SEQ_HANDLE bleio_seq_handle, + void* context, + const char* characteristic_uuid, + BLEIO_SEQ_INSTRUCTION_TYPE type, + BLEIO_SEQ_RESULT result, + BUFFER_HANDLE data +); + +/** + * Callback invoked when the sequencer completes a write operation. + */ +typedef void(*ON_BLEIO_SEQ_WRITE_COMPLETE)( + BLEIO_SEQ_HANDLE bleio_seq_handle, + void* context, + const char* characteristic_uuid, + BLEIO_SEQ_INSTRUCTION_TYPE type, + BLEIO_SEQ_RESULT result +); + +/** +* Callback invoked when the sequence has been destroyed. +*/ +typedef void(*ON_BLEIO_SEQ_DESTROY_COMPLETE)(BLEIO_SEQ_HANDLE bleio_seq_handle, void* context); + +extern BLEIO_SEQ_HANDLE BLEIO_Seq_Create( + BLEIO_GATT_HANDLE bleio_gatt_handle, + VECTOR_HANDLE instructions, + ON_BLEIO_SEQ_READ_COMPLETE on_read_complete, + ON_BLEIO_SEQ_WRITE_COMPLETE on_write_complete +); + +extern void BLEIO_Seq_Destroy( + BLEIO_SEQ_HANDLE bleio_seq_handle, + ON_BLEIO_SEQ_DESTROY_COMPLETE on_destroy_complete, + void* context +); + +extern BLEIO_SEQ_RESULT BLEIO_Seq_Run(BLEIO_SEQ_HANDLE bleio_seq_handle); + +extern BLEIO_SEQ_RESULT BLEIO_Seq_AddInstruction( + BLEIO_SEQ_HANDLE bleio_seq_handle, + BLEIO_SEQ_INSTRUCTION* instruction +); +``` + +## BLEIO_Seq_Create +```c +extern BLEIO_SEQ_HANDLE BLEIO_Seq_Create( + BLEIO_GATT_HANDLE bleio_gatt_handle, + VECTOR_HANDLE instructions, + ON_BLEIO_SEQ_READ_COMPLETE on_read_complete, + ON_BLEIO_SEQ_WRITE_COMPLETE on_write_complete +); +``` + +**SRS_BLEIO_SEQ_13_001: [** `BLEIO_Seq_Create` shall return `NULL` if `bleio_gatt_handle` is `NULL`. **]** + +**SRS_BLEIO_SEQ_13_002: [** `BLEIO_Seq_Create` shall return `NULL` if `instructions` is `NULL`. **]** + +**SRS_BLEIO_SEQ_13_003: [** `BLEIO_Seq_Create` shall return `NULL` if the vector `instructions` is empty. **]** + +**SRS_BLEIO_SEQ_13_004: [** `BLEIO_Seq_Create` shall return `NULL` if any of the underlying platform calls fail. **]** + +**SRS_BLEIO_SEQ_13_005: [** `BLEIO_Seq_Create` shall return a non-`NULL` handle on successful execution. **]** + +**SRS_BLEIO_SEQ_13_023: [** `BLEIO_Seq_Create` shall return `NULL` if a `READ_PERIODIC` instruction's `interval_in_ms` field is zero. **]** + +**SRS_BLEIO_SEQ_13_024: [** `BLEIO_Seq_Create` shall return `NULL` if a `WRITE_AT_INIT` or a `WRITE_AT_EXIT` or a `WRITE_ONCE` instruction has a `NULL` value in the `buffer` field. **]** + +**SRS_BLEIO_SEQ_13_025: [** `BLEIO_Seq_Create` shall return `NULL` if the `characteristic_uuid` field for any instruction is `NULL` or empty. **]** + +**SRS_BLEIO_SEQ_13_028: [** On Windows, this function shall return `NULL`. **]** + +## BLEIO_Seq_Destroy +```c +extern void BLEIO_Seq_Destroy( + BLEIO_SEQ_HANDLE bleio_seq_handle, + ON_BLEIO_SEQ_DESTROY_COMPLETE on_destroy_complete, + void* context +); +``` + +**SRS_BLEIO_SEQ_13_007: [** If `bleio_seq_handle` is `NULL` then `BLEIO_Seq_Destroy` shall do nothing. **]** + +**SRS_BLEIO_SEQ_13_006: [** `BLEIO_Seq_Destroy` shall free all resources associated with the handle once all the pending I/O operations are complete. **]** + +**SRS_BLEIO_SEQ_13_011: [** `BLEIO_Seq_Destroy` shall schedule the execution of all `WRITE_AT_EXIT` instructions. +> **Note:** *This is a 'best effort' write attempt. If this write operation fails then that fact is logged and no further action is taken.* **]** + +**SRS_BLEIO_SEQ_13_021: [** When the `WRITE_AT_EXIT` instruction completes execution this API shall invoke the `on_write_complete` callback passing in the status of the operation and the callback context that was passed in via the `BLEIO_SEQ_INSTRUCTION` structure. **]** + +**SRS_BLEIO_SEQ_13_027: [** When the `WRITE_AT_EXIT` instruction completes execution this API shall free the buffer that was passed in via the instruction. **]** + +**SRS_BLEIO_SEQ_13_009: [** If there are active instructions of type `READ_PERIODIC` in progress then the timers associated with those instructions shall be cancelled. **]** + +**SRS_BLEIO_SEQ_13_029: [** On Windows, this function shall do nothing. **]** + +**SRS_BLEIO_SEQ_13_031: [** If `on_destroy_complete` is not `NULL` then `BLEIO_Seq_Destroy` shall invoke `on_destroy_complete` once all `WRITE_AT_EXIT` instructions have been executed. **]** + +**SRS_BLEIO_SEQ_13_032: [** If `on_destroy_complete` is not `NULL` then `BLEIO_Seq_Destroy` shall pass `context` as-is to `on_destroy_complete`. **]** + +## BLEIO_Seq_Run +```c +extern BLEIO_SEQ_RESULT BLEIO_Seq_Run(BLEIO_SEQ_HANDLE bleio_seq_handle); +``` + +**SRS_BLEIO_SEQ_13_010: [** `BLEIO_Seq_Run` shall return `BLEIO_SEQ_ERROR` if `bleio_seq_handle` is `NULL`. **]** + +**SRS_BLEIO_SEQ_13_013: [** `BLEIO_Seq_Run` shall return `BLEIO_SEQ_ERROR` if `BLEIO_Seq_Run` was previously called on this handle. **]** + +**SRS_BLEIO_SEQ_13_014: [** `BLEIO_Seq_Run` shall return `BLEIO_SEQ_ERROR` if an underlying platform call fails. **]** + +**SRS_BLEIO_SEQ_13_016: [** `BLEIO_Seq_Run` shall schedule execution of all `WRITE_AT_INIT` instructions. **]** + +**SRS_BLEIO_SEQ_13_015: [** `BLEIO_Seq_Run` shall schedule execution of all `READ_ONCE` instructions. **]** + +**SRS_BLEIO_SEQ_13_033: [** `BLEIO_Seq_Run` shall schedule execution of all `WRITE_ONCE` instructions. **]** + +**SRS_BLEIO_SEQ_13_017: [** `BLEIO_Seq_Run` shall create timers at the specified intervals for scheduling execution of all `READ_PERIODIC` instructions. **]** + +**SRS_BLEIO_SEQ_13_018: [** When a `READ_ONCE` or a `READ_PERIODIC` instruction completes execution this API shall invoke the `on_read_complete` callback passing in the data that was read along with the status of the operation and the callback context that was passed in via the `BLEIO_SEQ_INSTRUCTION` structure. **]** + +**SRS_BLEIO_SEQ_13_020: [** When the `WRITE_AT_INIT` instruction completes execution this API shall invoke the `on_write_complete` callback passing in the status of the operation and the callback context that was passed in via the `BLEIO_SEQ_INSTRUCTION` structure. **]** + +**SRS_BLEIO_SEQ_13_026: [** When the `WRITE_AT_INIT` instruction completes execution this API shall free the buffer that was passed in via the instruction. **]** + +**SRS_BLEIO_SEQ_13_034: [** When the `WRITE_ONCE` instruction completes execution this API shall invoke the `on_write_complete` callback passing in the status of the operation and the callback context that was passed in via the `BLEIO_SEQ_INSTRUCTION` structure. **]** + +**SRS_BLEIO_SEQ_13_035: [** When the `WRITE_ONCE` instruction completes execution this API shall free the buffer that was passed in via the instruction. **]** + +**SRS_BLEIO_SEQ_13_030: [** On Windows this function shall return `BLEIO_SEQ_ERROR`. **]** + +## BLEIO_Seq_AddInstruction +```c +extern BLEIO_SEQ_RESULT BLEIO_Seq_AddInstruction( + BLEIO_SEQ_HANDLE bleio_seq_handle, + BLEIO_SEQ_INSTRUCTION* instruction +); +``` + +**SRS_BLEIO_SEQ_13_036: [** `BLEIO_Seq_AddInstruction` shall return `BLEIO_SEQ_ERROR` if `bleio_seq_handle` is `NULL`. **]** + +**SRS_BLEIO_SEQ_13_046: [** `BLEIO_Seq_AddInstruction` shall return `BLEIO_SEQ_ERROR` if `instruction` is `NULL`. **]** + +**SRS_BLEIO_SEQ_13_047: [** `BLEIO_Seq_AddInstruction` shall return `BLEIO_SEQ_ERROR` if a `READ_PERIODIC` instruction's `interval_in_ms` field is zero. **]** + +**SRS_BLEIO_SEQ_13_048: [** `BLEIO_Seq_AddInstruction` shall return `BLEIO_SEQ_ERROR` if a `WRITE_AT_INIT` or a `WRITE_AT_EXIT` or a `WRITE_ONCE` instruction has a `NULL` value in the `buffer` field. **]** + +**SRS_BLEIO_SEQ_13_049: [** `BLEIO_Seq_AddInstruction` shall return `BLEIO_SEQ_ERROR` if the `characteristic_uuid` field for the instruction is `NULL` or empty. **]** + +**SRS_BLEIO_SEQ_13_045: [** `BLEIO_Seq_AddInstruction` shall return `BLEIO_SEQ_ERROR` if `BLEIO_Seq_Run` was *NOT* called first. **]** + +**SRS_BLEIO_SEQ_13_037: [** `BLEIO_Seq_AddInstruction` shall return `BLEIO_SEQ_ERROR` if an underlying platform call fails. **]** + +**SRS_BLEIO_SEQ_13_038: [** `BLEIO_Seq_AddInstruction` shall schedule execution of the instruction. **]** + +**SRS_BLEIO_SEQ_13_039: [** `BLEIO_Seq_AddInstruction` shall create a timer at the specified interval if the instruction is a `READ_PERIODIC` instruction. **]** + +**SRS_BLEIO_SEQ_13_040: [** When a `READ_ONCE` or a `READ_PERIODIC` instruction completes execution this API shall invoke the `on_read_complete` callback passing in the data that was read along with the status of the operation and the callback context that was passed in via the `BLEIO_SEQ_INSTRUCTION` structure. **]** + +**SRS_BLEIO_SEQ_13_041: [** When a `WRITE_AT_INIT` or a `WRITE_ONCE` instruction completes execution this API shall free the buffer that was passed in via the instruction. **]** + +**SRS_BLEIO_SEQ_13_042: [** When a `WRITE_ONCE` or a `WRITE_AT_INIT` instruction completes execution this API shall invoke the `on_write_complete` callback passing in the status of the operation and the callback context that was passed in via the `BLEIO_SEQ_INSTRUCTION` structure. **]** + +**SRS_BLEIO_SEQ_13_044: [** On Windows this function shall return `BLEIO_SEQ_ERROR`. **]** \ No newline at end of file diff --git a/modules/ble/devdoc/blemodule_hl_requirements.md b/modules/ble/devdoc/blemodule_hl_requirements.md new file mode 100644 index 00000000..2688f317 --- /dev/null +++ b/modules/ble/devdoc/blemodule_hl_requirements.md @@ -0,0 +1,196 @@ +# Bluetooth Low Energy High Level Module + +## References + +[BLE Module Requirements](./blemodule_requirements.md) + +## Overview + +This module is a *passthrough* implementation that simply wraps the BLE Module code by providing an easy to use JSON based configuration interface. It de-serializes the JSON into a `BLE_CONFIG` instance and passes control to the underlying implementation. This module shall also accept cloud to device messages and transform cloud messages into a structure understood by the BLE module. + +### Receiving messages on the message bus +The module identifies the messages that it needs to process by the following +properties that must exist: + +>| PropertyName | Description | +>|--------------|------------------------------------------------------------------------------| +>| macAddress | The MAC address of a sensor, in canonical form | +>| source | The source property shall be set to "mapping" | + +The message content is a JSON object of the format: +```json +{ + "type": "write_once", + "characteristic_uuid": "", + "data": "" +} +``` + +## BLE_HL_Create +```c +MODULE_HANDLE BLE_HL_Create(MESSAGE_BUS_HANDLE bus, const void* configuration) +``` + +Creates a new BLE Module HL instance. `configuration` is a `const char*` that contains a JSON string that's typically passed through via the high level Gateway API `Gateway_Create_From_JSON`. The JSON object should conform to the following structure: + +``` +{ + /** + * Index of the BLE controller hardware on the device. + */ + "controller_index": 0, + + /** + * MAC address of the BLE device to connect to. + */ + "device_mac_address": "AA:BB:CC:DD:EE:FF", + + /** + * One or more instructions to be sent to the BLE device. + */ + "instructions": [ + { + /** + * The instruction type that maps to the `BLEIO_SEQ_INSTRUCTION_TYPE` + * enumeration from `bleio_seq.h`. The 'type' property can be one of + * the following: `read_once`, `read_periodic`, `write_at_init` and + * `write_at_exit` each mapping respectively to the `READ_ONCE`, + * `READ_PERIODIC`, `WRITE_AT_INIT` and `WRITE_AT_EXIT` values from + * the `BLEIO_SEQ_INSTRUCTION_TYPE` enumeration. + */ + "type": "read_once", + + /** + * The GATT characteristic's UUID string. + */ + "characteristic_uuid": "00002A24-0000-1000-8000-00805F9B34FB" + }, + { + "type": "read_periodic", + "characteristic_uuid": "F000AA01-0451-4000-B000-000000000000", + + /** + * For a `read_periodic` instruction this value specifies the + * sampling frequency for the characteristic in question given + * in milliseconds. + */ + "interval_in_ms": 1000 + }, + { + "type": "write_at_init", + "characteristic_uuid": "F000AA02-0451-4000-B000-000000000000", + + /** + * For `write_at_init` and `write_at_exit` instructions this value + * provides the data to be written to the device encoded as a Base64 + * string. + */ + "data": "AQ==" + }, + { + "type": "write_at_exit", + "characteristic_uuid": "F000AA02-0451-4000-B000-000000000000", + "data": "AA==" + } + ] +} +``` + +**SRS_BLE_HL_13_001: [** `BLE_HL_Create` shall return `NULL` if the `bus` or `configuration` parameters are `NULL`. **]** + +**SRS_BLE_HL_13_002: [** `BLE_HL_Create` shall return `NULL` if any of the underlying platform calls fail. **]** + +**SRS_BLE_HL_13_003: [** `BLE_HL_Create` shall return `NULL` if the JSON does not start with an `object`. **]** + +**SRS_BLE_HL_13_004: [** `BLE_HL_Create` shall return `NULL` if there is no `device_mac_address` property in the JSON. **]** + +**SRS_BLE_HL_13_013: [** `BLE_HL_Create` shall return `NULL` if the `device_mac_address` property's value is not a well-formed MAC address. **]** + +**SRS_BLE_HL_13_005: [** `BLE_HL_Create` shall return `NULL` if the `controller_index` value in the JSON is less than zero. **]** + +**SRS_BLE_HL_13_006: [** `BLE_HL_Create` shall return `NULL` if the `instructions` array does not exist in the JSON. **]** + +**SRS_BLE_HL_13_020: [** `BLE_HL_Create` shall return `NULL` if the `instructions` array length is equal to zero. **]** + +**SRS_BLE_HL_13_007: [** `BLE_HL_Create` shall return `NULL` if each instruction is not an `object`. **]** + +**SRS_BLE_HL_13_008: [** `BLE_HL_Create` shall return `NULL` if a given instruction does not have a `type` property. **]** + +**SRS_BLE_HL_13_021: [** `BLE_HL_Create` shall return `NULL` if a given instruction's `type` property is unrecognized. **]** + +**SRS_BLE_HL_13_009: [** `BLE_HL_Create` shall return `NULL` if a given instruction does not have a `characteristic_uuid` property. **]** + +**SRS_BLE_HL_13_010: [** `BLE_HL_Create` shall return `NULL` if the `interval_in_ms` value for a `read_periodic` instruction isn't greater than zero. **]** + +**SRS_BLE_HL_13_011: [** `BLE_HL_Create` shall return `NULL` if an instruction of type `write_at_init` or `write_at_exit` does not have a `data` property. **]** + +**SRS_BLE_HL_13_012: [** `BLE_HL_Create` shall return `NULL` if an instruction of type `write_at_init` or `write_at_exit` has a `data` property whose value does not decode successfully from base 64. **]** + +**SRS_BLE_HL_13_014: [** `BLE_HL_Create` shall call the underlying module's 'create' function. **]** + +**SRS_BLE_HL_13_022: [** `BLE_HL_Create` shall return `NULL` if calling the underlying module's `create` function fails. **]** + +**SRS_BLE_HL_13_023: [** `BLE_HL_Create` shall return a non-`NULL` handle if calling the underlying module's `create` function succeeds. **]** + +## BLE_HL_Destroy +```c +void BLE_HL_Destroy(MODULE_HANDLE module) +``` + +**SRS_BLE_HL_13_017: [** `BLE_HL_Destroy` shall do nothing if `module` is `NULL`. **]** + +**SRS_BLE_HL_13_015: [** `BLE_HL_Destroy` shall destroy all used resources. **]** + +## BLE_HL_Receive +```c +void BLE_HL_Receive(MODULE_HANDLE module, MESSAGE_HANDLE message_handle) +``` + +**SRS_BLE_HL_13_016: [** `BLE_HL_Receive` shall do nothing if `module` is `NULL`. **]** + +**SRS_BLE_HL_17_001: [** `BLE_HL_Receive` shall do nothing if `message_handle` is `NULL`. **]** + +**SRS_BLE_HL_17_002: [** If `messageHandle` properties does not contain "macAddress" property, then this function shall return. **]** + +**SRS_BLE_HL_17_003: [** If `macAddress` of the message property does not match this module's MAC address, then this function shall return. **]** + +**SRS_BLE_HL_17_004: [** If `messageHandle` properties does not contain "source" property, then this function shall return. **]** + +**SRS_BLE_HL_17_005: [** If the `source` of the message properties is not "mapping", then this function shall return. **]** + +**SRS_BLE_HL_17_006: [** `BLE_HL_Receive` shall parse the message contents as a JSON object. **]** + +**SRS_BLE_HL_17_007: [** If the message contents do not parse, then `BLE_HL_Receive` shall return. **]** + +**SRS_BLE_HL_17_008: [** `BLE_HL_Receive` shall return if the JSON object does not contain the following fields: "type", "characteristic_uuid", and "data". **]** + +**SRS_BLE_HL_17_012: [** `BLE_HL_Receive` shall create a `STRING_HANDLE` from the characteristic_uuid data field. **]** + +**SRS_BLE_HL_17_013: [** If the string creation fails, `BLE_HL_Receive` shall return. **]** + +**SRS_BLE_HL_17_014: [** `BLE_HL_Receive` shall parse the json object to fill in a new BLE_INSTRUCTION. **]** + +**SRS_BLE_HL_17_026: [** If the json object does not parse, `BLE_HL_Receive` shall return. **]** + +**SRS_BLE_HL_17_016: [** `BLE_HL_Receive` shall set characteristic_uuid to the created STRING. **]** + +**SRS_BLE_HL_17_018: [** `BLE_HL_Receive` shall call `ConstMap_CloneWriteable` on the message properties. **]** + +**SRS_BLE_HL_17_019: [** If `ConstMap_CloneWriteable` fails, `BLE_HL_Receive` shall return. **]** + +**SRS_BLE_HL_17_020: [** `BLE_HL_Receive` shall call `Map_AddOrUpdate` with key of "source" and value of "BLE". **]** + +**SRS_BLE_HL_17_023: [** `BLE_HL_Receive` shall create a new message by calling `Message_Create` with new map and BLE_INSTRUCTION as the buffer. **]** + +**SRS_BLE_HL_17_024: [** If creating new message fails, `BLE_HL_Receive` shall deallocate all resources and return. **]** + +**SRS_BLE_HL_13_018: [** `BLE_HL_Receive` shall forward new message to the underlying module. **]** + +**SRS_BLE_HL_17_025: [** `BLE_HL_Receive` shall free all resources created. **]** + +## Module_GetAPIs +```c +extern const MODULE_APIS* Module_GetAPIS(void); +``` + +**SRS_BLE_HL_13_019: [** `Module_GetAPIS` shall return a non-`NULL` pointer to a structure of type `MODULE_APIS` that has all fields initialized to non-`NULL` values. **]** diff --git a/modules/ble/devdoc/blemodule_requirements.md b/modules/ble/devdoc/blemodule_requirements.md new file mode 100644 index 00000000..9d868d44 --- /dev/null +++ b/modules/ble/devdoc/blemodule_requirements.md @@ -0,0 +1,137 @@ +# Bluetooth Low Energy Module + +## Overview + +The Bluetooth Low Energy (BLE) module is a general purpose Gateway module that can be used to execute *read* and *write* I/O operations on a BLE device. The I/O operations are expressed in the form of *instructions* that are to be executed. Each instruction has a *type* associated with it and allows for the following use cases: + + - Read some data once + - Write some data once + - Read some data at a specified frequency + - Write some data when the module starts running + - Write some data when the module terminates + +The I/O operations are specified in the context of a [GATT](https://learn.adafruit.com/introduction-to-bluetooth-low-energy/gatt) characteristic, i.e., the reads and writes are from/to GATT characteristics. + +## Types + +```c +typedef struct BLE_INSTRUCTION_TAG +{ + /** + * The type of instruction this is from the BLEIO_SEQ_INSTRUCTION_TYPE enum. + */ + BLEIO_SEQ_INSTRUCTION_TYPE instruction_type; + + /** + * The GATT characteristic to read from/write to. + */ + STRING_HANDLE characteristic_uuid; + + union + { + /** + * If 'instruction_type' is equal to READ_PERIODIC then this + * value indicates the polling interval in milliseconds. + */ + uint32_t interval_in_ms; + + /** + * If 'instruction_type' is equal to WRITE_AT_INIT or WRITE_AT_EXIT + * then this is the buffer that is to be written. + */ + BUFFER_HANDLE buffer; + }data; +}BLE_INSTRUCTION; + +typedef struct BLE_CONFIG_TAG +{ + BLE_DEVICE_CONFIG device_config; // BLE device information + VECTOR_HANDLE instructions; // array of BLE_INSTRUCTION objects to be executed +}BLE_CONFIG; + +typedef struct BLE_HANDLE_DATA_TAG +{ + MESSAGE_BUS_HANDLE bus; + BLE_DEVICE_CONFIG device_config; + BLEIO_GATT_HANDLE bleio_gatt; + BLEIO_SEQ_HANDLE bleio_seq; +}BLE_HANDLE_DATA; +``` + +## BLE_Create +```c +MODULE_HANDLE BLE_Create(MESSAGE_BUS_HANDLE bus, const void* configuration); +``` + +Creates a new BLE module instance. The parameter `configuration` is a pointer to a `BLE_CONFIG` object. + +**SRS_BLE_13_001: [** `BLE_Create` shall return `NULL` if `bus` is `NULL`. **]** + +**SRS_BLE_13_002: [** `BLE_Create` shall return `NULL` if `configuration` is `NULL`. **]** + +**SRS_BLE_13_003: [** `BLE_Create` shall return `NULL` if `configuration->instructions` is `NULL`. **]** + +**SRS_BLE_13_004: [** `BLE_Create` shall return `NULL` if the `configuration->instructions` vector is empty (size is zero). **]** + +**SRS_BLE_13_005: [** `BLE_Create` shall return `NULL` if an underlying API call fails. **]** + +**SRS_BLE_13_006: [** `BLE_Create` shall return a non-`NULL` `MODULE_HANDLE` when successful. **]** + +**SRS_BLE_13_009: [** `BLE_Create` shall allocate memory for an instance of the `BLE_HANDLE_DATA` structure and use that as the backing structure for the module handle. **]** + +**SRS_BLE_13_008: [** `BLE_Create` shall create and initialize the `bleio_gatt` field in the `BLE_HANDLE_DATA` object by calling `BLEIO_gatt_create`. **]** + +**SRS_BLE_13_010: [** `BLE_Create` shall create and initialize the `bleio_seq` field in the `BLE_HANDLE_DATA` object by calling `BLEIO_Seq_Create`. **]** + +**SRS_BLE_13_011: [** `BLE_Create` shall asynchronously open a connection to the BLE device by calling `BLEIO_gatt_connect`. **]** + +**SRS_BLE_13_012: [** `BLE_Create` shall return `NULL` if `BLEIO_gatt_connect` returns a non-zero value. **]** + +**SRS_BLE_13_014: [** If the asynchronous call to `BLEIO_gatt_connect` is successful then the `BLEIO_Seq_Run` function shall be called on the `bleio_seq` field from `BLE_HANDLE_DATA`. **]** + +**SRS_BLE_13_019: [** `BLE_Create` shall handle the `ON_BLEIO_SEQ_READ_COMPLETE` callback on the BLE I/O sequence. If the call is successful then a new message shall be published on the message bus with the buffer that was read as the content of the message along with the following properties: + +>| Property Name | Description | +>|-------------------------|---------------------------------------------------------------| +>| ble_controller_index | The index of the bluetooth radio hardware on the device. | +>| mac_address | MAC address of the BLE device from which the data was read. | +>| timestamp | Timestamp indicating when the data was read. | +>| source | This property will always have the value `bleTelemetry`. | + +**]** + +## BLE_Receive +```c +void BLE_Receive(MODULE_HANDLE module, MESSAGE_HANDLE message); +``` + +**SRS_BLE_13_018: [** `BLE_Receive` shall do nothing if `module` is `NULL` or if `message` is `NULL`. **]** + +**SRS_BLE_13_020: [** `BLE_Receive` shall ignore all messages except those that have the following properties: + +>| Property Name | Description | +>|-------------------------|-------------------------------------------------------------------------| +>| source | This property should have the value `bleCommand`. | +>| macAddress | MAC address of the BLE device to which the data to should be written. | + +**]** + +**SRS_BLE_13_022: [** `BLE_Receive` shall ignore the message unless the 'macAddress' property matches the MAC address that was passed to this module when it was created. **]** + +**SRS_BLE_13_021: [** `BLE_Receive` shall treat the content of the message as a `BLE_INSTRUCTION` and schedule it for execution by calling `BLEIO_Seq_AddInstruction`. **]** + +## BLE_Destroy +```c +void BLE_Destroy(MODULE_HANDLE module); +``` + +**SRS_BLE_13_016: [** If `module` is `NULL` `BLE_Destroy` shall do nothing. **]** + +**SRS_BLE_13_017: [** `BLE_Destroy` shall free all resources. **]** + +## Module_GetAPIs +```c +extern const MODULE_APIS* Module_GetAPIS(void); +``` + +**SRS_BLE_13_007: [** `Module_GetAPIS` shall return a non-`NULL` pointer to a structure of type `MODULE_APIS` that has all fields initialized to non-`NULL` values. **]** diff --git a/modules/ble/devdoc/gio_async_seq_hld.md b/modules/ble/devdoc/gio_async_seq_hld.md new file mode 100644 index 00000000..381dad00 --- /dev/null +++ b/modules/ble/devdoc/gio_async_seq_hld.md @@ -0,0 +1,287 @@ +# GIO Asynchronous Sequence API + +## Overview + +The [GNOME GIO](https://developer.gnome.org/gio/2.46/) library provides APIs for dealing with various I/O operations in a given application. We use GIO's implementation of the [D-Bus](https://www.freedesktop.org/wiki/Software/dbus/) message bus system in order to communicate with the D-Bus objects that [BlueZ](http://www.bluez.org) - the *Bluetooth* stack implemenation on Linux - provides. Given that almost all D-Bus API calls are asynchronous, it proves to be useful to build an abstraction that allows us to succinctly express in code the intention that a set of asynchronous operations should be performed in sequence with support for early bail out in case one of the asynchronous operations produce an error. This document describes how this implementation works and helps navigate the implementation source of the API. + +## What Does A Typical GIO Async API Call Look Like? + +Let's take a look at an example of calling the GIO D-Bus API to acquire a reference to the system bus instance (as opposed to the session bus) asynchronously. + +```c +typedef struct tagAPP_CONTEXT +{ + GDBusConnection* bus; // the connection to the system bus + + //------------------------------ + // other application state here + //------------------------------ + +}APP_CONTEXT; + +int main() +{ + APP_CONTEXT context = { 0 }; + + // open a connection to the system bus + g_bus_get( + G_BUS_TYPE_SYSTEM, // we want the system bus + NULL, // this is not cancellable + on_bus_connect, // connect callback + &context + ); + + // run the glib loop + loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); + + return 0; +} +``` + +`g_bus_get` is a part of the D-Bus implementation in GIO. It asynchronously attempts to get a connection to a system/session bus (the system bus in this case) and provides a pointer to it via the callback supplied to it - in this case a function called `on_bus_connect`. Here's what `on_bus_connect` would look like: + +```c +static void on_bus_connect( + GObject* source_object, + GAsyncResult* result, + gpointer user_data +) +{ + APP_CONTEXT* context = (APP_CONTEXT*)user_data; + GError* error = NULL; + context->bus = g_bus_get_finish(result, &error); + if (context->bus != NULL && error == NULL) + { + //------------------------------ + // do something with the bus here + //------------------------------ + } + else + { + printf("Whoops! No bus for you. Error: %s\r\n", error->message); + g_clear_error(&error); + } +} +``` + +`g_bus_get_finish` is responsible for transforming the `GAsyncResult` pointer into a `GDBusConnection` pointer. Turns out that pretty much all asynchronous I/O APIs in GIO support this programming pattern, including, significantly, the requirement that all callbacks must necessarily match the signature as shown for the `on_bus_connect` function above. GIO defines a type called [`GAsyncReadyCallback`](https://developer.gnome.org/gio/stable/GAsyncResult.html#GAsyncReadyCallback) for this function signature like so: + +```c +void +(*GAsyncReadyCallback) (GObject *source_object, + GAsyncResult *res, + gpointer user_data); +``` + +We take advantage of this to automate the task of running a set of asynchronous operations in sequence. + +## The GIO Async Sequence + +### Creating The Sequence + +A set of operations to be asynchronously run in sequence is represented using a `GIO_ASYNCSEQ_HANDLE` handle. One creates an instance of this like so: + +```c +// +// this is some context information that will be made available +// from the GIO_ASYNCSEQ_HANDLE via the GIO_Async_Seq_GetContext API +// +MY_APP_CONTEXT* context = (MY_APP_CONTEXT*)malloc(sizeof(MY_APP_CONTEXT)); + +GIO_ASYNCSEQ_HANDLE async_seq = GIO_Async_Seq_Create( + context, + on_sequence_error, + on_sequence_complete +); +``` + +Here, `on_sequence_error` and `on_sequence_complete` are callback functions that look like this: + +```c +static void on_sequence_error(GIO_ASYNCSEQ_HANDLE async_seq_handle, const GError* error) +{ +} + +static void on_sequence_complete(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result) +{ +} +``` + +`on_sequence_error` is invoked when any of the asynchronous operations in the sequence fails. `on_sequence_complete` is called when all the asynchronous operations in the sequence run to completion successfully. The `context` parameter is a pointer that is cached by the sequence and is accessible at any point by calling `GIO_Async_Seq_GetContext`. One might use this pointer to store contextual data that is relevant to the async operations in the sequence. + +### Adding Async Operations To The Sequence + +Once the sequence handle has been instantiated, async operations can be added to the sequence via the `GIO_Async_Seq_Add` API. But what exactly is an *asynchronous operation*? + +#### The (start, finish) Function Pair + +If you look at the code snippet in the GIO async API example above, you'll note that we have a pair of functions being called - one to kick off the async operation and another one to *complete* the async call and fetch the results. In the code above `g_bus_get` gets the async call started and `g_bus_get_finish` is invoked to fetch the result of the call. In case an error occurs then `g_bus_get_finish` populates the `GError*` pointer with a valid error object. + +In the GIO Async Sequence API we consider this pair of functions as defining a single async operation, i.e., users of this API will need to provide two functions for every async operation - a *start* function and a *finish* function. The start and the finish functions should match the function signatures as shown below: + +```c +/** + * The 'start' function. + */ +typedef void (*GIO_ASYNCSEQ_CALLBACK)( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +); + +/** + * The 'finish' function. + */ +typedef gpointer (*GIO_ASYNCSEQ_FINISH_CALLBACK)( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + GAsyncResult* result, + GError** error +); +``` + +For example, taking the same example of acquiring a reference to the system bus, here's how you would write it when using the sequencer API: + +
+static void connect_system_bus(
+    GIO_ASYNCSEQ_HANDLE async_seq_handle,
+    gpointer previous_result,
+    gpointer callback_context,
+    GAsyncReadyCallback async_callback
+)
+{
+    g_bus_get(
+        G_BUS_TYPE_SYSTEM,
+        NULL,
+        async_callback,
+        async_seq_handle
+    );
+}
+
+static gpointer connect_system_bus_finish(
+    GIO_ASYNCSEQ_HANDLE async_seq_handle,
+    GAsyncResult* result,
+    GError** error
+)
+{
+    return g_bus_get_finish(result, error);
+}
+
+ +Take note of the code in **bold**. For the callback to the GIO D-Bus API (`g_bus_get`), we use the callback parameter that's supplied as an input to `connect_system_bus` - `async_callback`. This is how the sequencer API knows when the next async operation in the sequence should be invoked. We also pass the handle to the async sequence (`async_seq_handle`) as the callback context when invoking `g_bus_get`. This is also a pre-requisite in order for the async sequence API to function - passing any other data as context will result in undefined behavior. Passing `NULL` for this context will cause the sequence to end in an error state (and call the sequence's error callback which was registered in the `GIO_Async_Seq_Create` call). + +And finally, the `connect_system_bus` function receives a `gpointer` parameter called `previous_result` which is a pointer to the result obtained from the previous async call in the sequence. In this example, since this is the first call in the sequence this parameter will have the value `NULL`. But for subsequent calls in the sequence this should typically have a non-`NULL` value (unless the value `NULL` *is* the result of the call). + +#### The Add API + +Now that you have your async operations defined, you'd add them to the sequence like so: + +```c +GIO_ASYNCSEQ_RESULT seq_result = GIO_Async_Seq_Add( + async_seq, NULL, + + // + // connect to the system bus + // + connect_system_bus, + connect_system_bus_finish, + + // + // create an instance of the d-bus object manager + // + create_object_manager, + create_object_manager_finish, + + // + // create an instance of a device proxy object + // + create_device_proxy, + create_device_proxy_finish, + + // + // connect to the device + // + connect_device, + connect_device_finish, + + // + // sentinel value to signal end of sequence + // + NULL +); + +if (seq_result == GIO_ASYNCSEQ_OK) +{ + // all good +} +else +{ + // not so good +} +``` + +`GIO_Async_Seq_Add` is a variable arguments function and is declared like so: + +```c +extern GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Add( + GIO_ASYNCSEQ_HANDLE async_seq_handle, // handle to the async sequence + + gpointer callback_context, // context for this set of operations + + ... // one or more pairs of start/finish + // functions ending in a single `NULL` + // parameter signalling the ending of + // the operations in this call +); +``` + +Each async operation - which is a pair of functions - needs to be passed via the variable arguments part of the function call. `callback_context` is context data specific to all the async operations in this call which is subsequently passed as the `callback_context` parameter to the *start* function of each operation. + +### Running The Sequence + +Running the sequence is fairly trivial. You simply call the `GIO_Async_Seq_Run_Async` function like so: + +```c +if(GIO_Async_Seq_Run_Async(context->async_seq) == GIO_ASYNCSEQ_OK) +{ + // all good +} +else +{ + // not so good +} +``` + +## So How Does All This Work? + +As you might imagine the async sequence API maintains a list of async operations in a `GPtrArray` object. Each element in the array is an instance of a structure called `GIO_ASYNCSEQ_CALLBACK_DATA` which is defined like so: + +```c +typedef struct GIO_ASYNCSEQ_CALLBACK_DATA_TAG +{ + GIO_ASYNCSEQ_CALLBACK callback; // callback to be invoked + + GIO_ASYNCSEQ_FINISH_CALLBACK finish_callback; // callback to be invoked for finishing an + // async call and fetching the result/error + + gpointer callback_context; // context to be supplied for this callback +}GIO_ASYNCSEQ_CALLBACK_DATA; +``` + +When you call `GIO_Async_Seq_Run_Async` it fetches the first async operation from the array and invokes it's *start* function. In case you'd forgotten, here's the definition of the *start* function pointer type: + +```c +typedef void (*GIO_ASYNCSEQ_CALLBACK)( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +); +``` + +For the last parameter to this function - `async_callback` - we pass a pointer to an internal static function defined in the async sequence API's implementation. Let's call it `resolve_callback`. The expectation is that the *start* function will pass this pointer along to the underlying async GIO API as the callback to be invoked when the async operation completes. When `resolve_callback` is invoked we call the *finish* callback of the currently executing async operation and then call the *start* function of the next async operation. When there are no more async operations to call, `resolve_callback` invokes the *complete* callback for the entire sequence (which was registered when `GIO_Async_Seq_Create` was called). If any of the *finish* callbacks return a non-`NULL` error pointer then the sequence is considered as having failed and the sequence's error callback is invoked (which, again, was registered when `GIO_Async_Seq_Create` was called). + +Here's a sequence diagram depicting the control flow of an asynchronous sequence containing two asynchronous operations where everything works and there are no errors. The alternate control flow, i.e., when an error occurs, looks very similar to this diagram except that the sequence's error callback would get called immediately as soon as the error occurs instead of the sequence's complete callback at the end of the sequence. + +>![GIO Async API sequence diagram for happy path](./gio_async_seq_sequence_diagram.png) diff --git a/modules/ble/devdoc/gio_async_seq_requirements.md b/modules/ble/devdoc/gio_async_seq_requirements.md new file mode 100644 index 00000000..f383371e --- /dev/null +++ b/modules/ble/devdoc/gio_async_seq_requirements.md @@ -0,0 +1,214 @@ +## Overview + +The [GNOME GIO](https://developer.gnome.org/gio/2.46/) library provides APIs for dealing with various I/O operations in a given application. We use GIO's implementation of the [D-Bus](https://www.freedesktop.org/wiki/Software/dbus/) message bus system in order to communicate with the D-Bus objects that [BlueZ](http://www.bluez.org) - the *Bluetooth* stack implemenation on Linux - provides. Given that almost all D-Bus API calls are asynchronous, it proves to be useful to build an abstraction that allows us to succinctly express in code the intention that a set of asynchronous operations should be performed in sequence with support for early bail out in case one of the asynchronous operations produce an error. This document specifies the requirements for all the APIs provided by this library. + +## References + +- [GIO Asynchronous Sequence API - High Level Design Document](./gio_async_seq_hld.md) +- [GNOME GIO](https://developer.gnome.org/gio/2.46/) + +## Types + +```c +typedef struct GIO_ASYNCSEQ_HANDLE_DATA_TAG* GIO_ASYNCSEQ_HANDLE; + +#define GIO_ASYNCSEQ_RESULT_VALUES \ + GIO_ASYNCSEQ_ERROR, \ + GIO_ASYNCSEQ_OK + +DEFINE_ENUM(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_RESULT_VALUES); + +#define GIO_ASYNCSEQ_STATE_VALUES \ + GIO_ASYNCSEQ_STATE_PENDING, \ + GIO_ASYNCSEQ_STATE_RUNNING, \ + GIO_ASYNCSEQ_STATE_COMPLETE, \ + GIO_ASYNCSEQ_STATE_ERROR + +DEFINE_ENUM(GIO_ASYNCSEQ_STATE, GIO_ASYNCSEQ_STATE_VALUES); + +typedef void (*GIO_ASYNCSEQ_CALLBACK)( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +); + +typedef void (*GIO_ASYNCSEQ_ERROR_CALLBACK)( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + const GError* error +); + +typedef gpointer (*GIO_ASYNCSEQ_FINISH_CALLBACK)( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + GAsyncResult* result, + GError** error +); + +typedef void(*GIO_ASYNCSEQ_COMPLETE_CALLBACK)( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result +); + +extern GIO_ASYNCSEQ_HANDLE GIO_Async_Seq_Create( + gpointer async_seq_context, + GIO_ASYNCSEQ_ERROR_CALLBACK error_callback, + GIO_ASYNCSEQ_COMPLETE_CALLBACK complete_callback +); + +extern void GIO_Async_Seq_Destroy(GIO_ASYNCSEQ_HANDLE async_seq_handle); + +extern gpointer GIO_Async_Seq_GetContext( + GIO_ASYNCSEQ_HANDLE async_seq_handle +); + +extern GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Add( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer callback_context, + ... +); + +extern GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Run_Async( + GIO_ASYNCSEQ_HANDLE async_seq_handle +); +``` + +## GIO_Async_Seq_Create +```c +GIO_ASYNCSEQ_HANDLE GIO_Async_Seq_Create( + gpointer async_seq_context, + GIO_ASYNCSEQ_ERROR_CALLBACK error_callback, + GIO_ASYNCSEQ_COMPLETE_CALLBACK complete_callback +); +``` + +**SRS_GIO_ASYNCSEQ_13_001: [** `GIO_Async_Seq_Create` shall return a non-`NULL` handle on successful execution. **]** + +**SRS_GIO_ASYNCSEQ_13_002: [** `GIO_Async_Seq_Create` shall return `NULL` when any of the underlying platform calls fail. **]** + +**SRS_GIO_ASYNCSEQ_13_005: [** `GIO_Async_Seq_Create` shall save the `async_seq_context` pointer so that it can be retrieved later by calling `GIO_Async_Seq_GetContext`. **]** + +## GIO_Async_Seq_Destroy +```c +void GIO_Async_Seq_Destroy(GIO_ASYNCSEQ_HANDLE async_seq_handle); +``` + +> If there are pending I/O operations in progress while this API is called no attempt is made to cancel/complete them. + +**SRS_GIO_ASYNCSEQ_13_003: [** `GIO_Async_Seq_Destroy` shall do nothing if `async_seq_handle` is `NULL`. **]** + +**SRS_GIO_ASYNCSEQ_13_004: [** `GIO_Async_Seq_Destroy` shall free all resources associated with the handle. **]** + +## GIO_Async_Seq_GetContext +```c +gpointer GIO_Async_Seq_GetContext(GIO_ASYNCSEQ_HANDLE async_seq_handle); +``` + +**SRS_GIO_ASYNCSEQ_13_006: [** `GIO_Async_Seq_GetContext` shall return `NULL` if `async_seq_handle` is `NULL`. **]** + +**SRS_GIO_ASYNCSEQ_13_007: [** `GIO_Async_Seq_GetContext` shall return the value of the `async_seq_context` parameter that was passed in when calling `GIO_Async_Seq_Create`. **]** + +## GIO_Async_Seq_Add + +```c +extern GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Add( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer callback_context, + ... +); +``` + +**SRS_GIO_ASYNCSEQ_13_008: [** `GIO_Async_Seq_Add` shall return `GIO_ASYNCSEQ_ERROR` if `async_seq_handle` is `NULL`. **]** + +**SRS_GIO_ASYNCSEQ_13_009: [** `GIO_Async_Seq_Add` shall return `GIO_ASYNCSEQ_ERROR` if the async sequence's state is not equal to `GIO_ASYNCSEQ_STATE_PENDING`. **]** + +**SRS_GIO_ASYNCSEQ_13_010: [** `GIO_Async_Seq_Add` shall *append* the new async operations to the end of the existing list of async operations if any. **]** + +**SRS_GIO_ASYNCSEQ_13_011: [** `GIO_Async_Seq_Add` shall add callbacks from the variable arguments list till a callback whose value is `NULL` is encountered. **]** + +**SRS_GIO_ASYNCSEQ_13_012: [** When a `GIO_ASYNCSEQ_CALLBACK` is encountered in the varargs, the next argument MUST be non-`NULL`. **]** + +**SRS_GIO_ASYNCSEQ_13_013: [** `GIO_Async_Seq_Add` shall return `GIO_ASYNCSEQ_ERROR` when any of the underlying platform calls fail. **]** + +**SRS_GIO_ASYNCSEQ_13_014: [** The `callback_context` pointer value shall be saved so that they can be passed to the callbacks later when they are invoked. **]** + +**SRS_GIO_ASYNCSEQ_13_015: [** The list of callbacks that had been added to the sequence before calling this API will remain unchanged after the function returns when an error occurs. **]** + +**SRS_GIO_ASYNCSEQ_13_016: [** `GIO_Async_Seq_Add` shall return `GIO_ASYNCSEQ_OK` if the API executes successfully. **]** + +## GIO_Async_Seq_Addv + +```c +extern GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Addv( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer callback_context, + va_list args_list +); +``` + +**SRS_GIO_ASYNCSEQ_13_035: [** `GIO_Async_Seq_Addv` shall return `GIO_ASYNCSEQ_ERROR` if `async_seq_handle` is `NULL`. **]** + +**SRS_GIO_ASYNCSEQ_13_036: [** `GIO_Async_Seq_Addv` shall return `GIO_ASYNCSEQ_ERROR` if the async sequence's state is not equal to `GIO_ASYNCSEQ_STATE_PENDING`. **]** + +**SRS_GIO_ASYNCSEQ_13_037: [** `GIO_Async_Seq_Addv` shall *append* the new async operations to the end of the existing list of async operations if any. **]** + +**SRS_GIO_ASYNCSEQ_13_038: [** `GIO_Async_Seq_Addv` shall add callbacks from the variable arguments list till a callback whose value is `NULL` is encountered. **]** + +**SRS_GIO_ASYNCSEQ_13_039: [** When a `GIO_ASYNCSEQ_CALLBACK` is encountered in the varargs, the next argument MUST be non-`NULL`. **]** + +**SRS_GIO_ASYNCSEQ_13_040: [** `GIO_Async_Seq_Addv` shall return `GIO_ASYNCSEQ_ERROR` when any of the underlying platform calls fail. **]** + +**SRS_GIO_ASYNCSEQ_13_041: [** The `callback_context` pointer value shall be saved so that they can be passed to the callbacks later when they are invoked. **]** + +**SRS_GIO_ASYNCSEQ_13_042: [** The list of callbacks that had been added to the sequence before calling this API will remain unchanged after the function returns when an error occurs. **]** + +**SRS_GIO_ASYNCSEQ_13_043: [** `GIO_Async_Seq_Addv` shall return `GIO_ASYNCSEQ_OK` if the API executes successfully. **]** + +## GIO_Async_Seq_Run_Async +```c +GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Run_Async(GIO_ASYNCSEQ_HANDLE async_seq_handle); +``` + +**SRS_GIO_ASYNCSEQ_13_017: [** `GIO_Async_Seq_Run_Async` shall return `GIO_ASYNCSEQ_ERROR` if `async_seq_handle` is `NULL`. **]** + +**SRS_GIO_ASYNCSEQ_13_018: [** `GIO_Async_Seq_Run_Async` shall return `GIO_ASYNCSEQ_ERROR` if the sequence's state is already `GIO_ASYNCSEQ_STATE_RUNNING`. **]** + +**SRS_GIO_ASYNCSEQ_13_019: [** `GIO_Async_Seq_Run_Async` shall complete the sequence and invoke the sequence's complete callback if there are no asynchronous operations to process. **]** + +**SRS_GIO_ASYNCSEQ_13_020: [** `GIO_Async_Seq_Run_Async` shall invoke the '*start*' callback of the first async operation in the sequence. **]** + +**SRS_GIO_ASYNCSEQ_13_021: [** `GIO_Async_Seq_Run_Async` shall pass the callback context that was supplied to `GIO_Async_Seq_Add` or `GIO_Async_Seq_Addv` when the first operation was added to the sequence when invoking the '*start*' callback. **]** + +**SRS_GIO_ASYNCSEQ_13_031: [** `GIO_Async_Seq_Run_Async` shall supply `NULL` as the value of the `previous_result` parameter of the '*start*' callback. **]** + +**SRS_GIO_ASYNCSEQ_13_022: [** `GIO_Async_Seq_Run_Async` shall return `GIO_ASYNCSEQ_OK` when there are no errors. **]** + +**SRS_GIO_ASYNCSEQ_13_023: [** `GIO_Async_Seq_Run_Async` shall supply a non-`NULL` pointer to a function as the value of the `async_callback` parameter when calling the '*start*' callback. **]** + +## resolve_callback +```c +static void resolve_callback( + GObject* source_object, + GAsyncResult* result, + gpointer user_data +); +``` + +**SRS_GIO_ASYNCSEQ_13_024: [** `resolve_callback` shall do nothing else if `user_data` is `NULL`. **]** + +**SRS_GIO_ASYNCSEQ_13_025: [** `resolve_callback` shall invoke the sequence's error callback and suspend execution of the sequence if the state of the sequence is not equal to `GIO_ASYNCSEQ_STATE_RUNNING`. **]** + +**SRS_GIO_ASYNCSEQ_13_026: [** `resolve_callback` shall invoke the '*finish*' callback of the async operation that was just concluded. **]** + +**SRS_GIO_ASYNCSEQ_13_027: [** `resolve_callback` shall invoke the sequence's error callback and suspend execution of the sequence if the '*finish*' callback returns a non-`NULL` `GError` pointer. **]** + +**SRS_GIO_ASYNCSEQ_13_028: [** `resolve_callback` shall free the `GError` object by calling `g_clear_error` if the '*finish*' callback returns a non-`NULL` `GError` pointer. **]** + +**SRS_GIO_ASYNCSEQ_13_029: [** `resolve_callback` shall invoke the '*start*' callback of the next async operation in the sequence. **]** + +**SRS_GIO_ASYNCSEQ_13_030: [** `resolve_callback` shall supply the result of calling the '*finish*' callback of the current async operation as the value of the `previous_result` parameter of the '*start*' callback of the next async operation. **]** + +**SRS_GIO_ASYNCSEQ_13_032: [** `resolve_callback` shall pass the callback context that was supplied to `GIO_Async_Seq_Add` when the next async operation was added to the sequence when invoking the '*start*' callback. **]** + +**SRS_GIO_ASYNCSEQ_13_033: [** `resolve_callback` shall supply a non-`NULL` pointer to a function as the value of the `async_callback` parameter when calling the '*start*' callback. **]** + +**SRS_GIO_ASYNCSEQ_13_034: [** `resolve_callback` shall complete the sequence and invoke the sequence's complete callback if there are no more asynchronous operations to process. **]** \ No newline at end of file diff --git a/modules/ble/devdoc/gio_async_seq_sequence_diagram.png b/modules/ble/devdoc/gio_async_seq_sequence_diagram.png new file mode 100644 index 00000000..ec2669fa Binary files /dev/null and b/modules/ble/devdoc/gio_async_seq_sequence_diagram.png differ diff --git a/modules/ble/devdoc/gio_async_seq_sequence_diagram.vsdx b/modules/ble/devdoc/gio_async_seq_sequence_diagram.vsdx new file mode 100644 index 00000000..8e1adb2d Binary files /dev/null and b/modules/ble/devdoc/gio_async_seq_sequence_diagram.vsdx differ diff --git a/modules/ble/inc/ble.h b/modules/ble/inc/ble.h new file mode 100644 index 00000000..822d1974 --- /dev/null +++ b/modules/ble/inc/ble.h @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef BLE_H +#define BLE_H + +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/strings.h" + +/** + * Including module.h dictates that: + * + * MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void); + * + * is implemented by the module. + */ +#include "module.h" + +#include "ble_gatt_io.h" +#include "bleio_seq.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct BLE_INSTRUCTION_TAG +{ + /** + * The type of instruction this is from the BLEIO_SEQ_INSTRUCTION_TYPE enum. + */ + BLEIO_SEQ_INSTRUCTION_TYPE instruction_type; + + /** + * The GATT characteristic to read from/write to. + */ + STRING_HANDLE characteristic_uuid; + + union + { + /** + * If 'instruction_type' is equal to READ_PERIODIC then this + * value indicates the polling interval in milliseconds. + */ + uint32_t interval_in_ms; + + /** + * If 'instruction_type' is equal to WRITE_AT_INIT or WRITE_AT_EXIT + * then this is the buffer that is to be written. + */ + BUFFER_HANDLE buffer; + }data; +}BLE_INSTRUCTION; + +typedef struct BLE_CONFIG_TAG +{ + BLE_DEVICE_CONFIG device_config; // BLE device information + VECTOR_HANDLE instructions; // array of BLE_INSTRUCTION objects to be executed +}BLE_CONFIG; + +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(BLE_MODULE)(void); + +#ifdef __cplusplus +} +#endif + +#endif /*BLE_H*/ diff --git a/modules/ble/inc/ble_gatt_io.h b/modules/ble/inc/ble_gatt_io.h new file mode 100644 index 00000000..6404336e --- /dev/null +++ b/modules/ble/inc/ble_gatt_io.h @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef BLE_GATT_IO_H +#define BLE_GATT_IO_H + +#include "azure_c_shared_utility/macro_utils.h" + +#ifdef __cplusplus +#include +#include +extern "C" { +#else +#include +#include +#endif /* __cplusplus */ + +/** +* The 6 byte mac address of the BLE device. +*/ +typedef struct BLE_MAC_ADDRESS_TAG +{ + uint8_t address[6]; +}BLE_MAC_ADDRESS; + +typedef struct BLE_DEVICE_CONFIG_TAG +{ + /** + * MAC address of the BLE device. + */ + BLE_MAC_ADDRESS device_addr; + + /** + * Zero based index of the bluetooth controller to be + * used. + */ + uint8_t ble_controller_index; +}BLE_DEVICE_CONFIG; + +typedef struct BLEIO_GATT_INSTANCE_TAG* BLEIO_GATT_HANDLE; + +#define BLEIO_GATT_RESULT_VALUES \ + BLEIO_GATT_OK, \ + BLEIO_GATT_ERROR + +DEFINE_ENUM(BLEIO_GATT_RESULT, BLEIO_GATT_RESULT_VALUES); + +#define BLEIO_GATT_STATE_VALUES \ + BLEIO_GATT_STATE_DISCONNECTED, \ + BLEIO_GATT_STATE_CONNECTING, \ + BLEIO_GATT_STATE_CONNECTED, \ + BLEIO_GATT_STATE_ERROR + +DEFINE_ENUM(BLEIO_GATT_STATE, BLEIO_GATT_STATE_VALUES); + +#define BLEIO_GATT_CONNECT_RESULT_VALUES \ + BLEIO_GATT_CONNECT_OK, \ + BLEIO_GATT_CONNECT_ERROR + +DEFINE_ENUM(BLEIO_GATT_CONNECT_RESULT, BLEIO_GATT_CONNECT_RESULT_VALUES); + +typedef void(*ON_BLEIO_GATT_CONNECT_COMPLETE)(BLEIO_GATT_HANDLE bleio_gatt_handle, void* context, BLEIO_GATT_CONNECT_RESULT connect_result); +typedef void(*ON_BLEIO_GATT_DISCONNECT_COMPLETE)(BLEIO_GATT_HANDLE bleio_gatt_handle, void* context); +typedef void(*ON_BLEIO_GATT_ATTRIB_READ_COMPLETE)(BLEIO_GATT_HANDLE bleio_gatt_handle, void* context, BLEIO_GATT_RESULT result, const unsigned char* buffer, size_t size); +typedef void(*ON_BLEIO_GATT_ATTRIB_WRITE_COMPLETE)(BLEIO_GATT_HANDLE bleio_gatt_handle, void* context, BLEIO_GATT_RESULT result); + +extern BLEIO_GATT_HANDLE BLEIO_gatt_create( + const BLE_DEVICE_CONFIG* config +); + +extern void BLEIO_gatt_destroy( + BLEIO_GATT_HANDLE bleio_gatt_handle +); + +extern int BLEIO_gatt_connect( + BLEIO_GATT_HANDLE bleio_gatt_handle, + ON_BLEIO_GATT_CONNECT_COMPLETE on_bleio_gatt_connect_complete, + void* callback_context +); + +extern void BLEIO_gatt_disconnect( + BLEIO_GATT_HANDLE bleio_gatt_handle, + ON_BLEIO_GATT_DISCONNECT_COMPLETE on_bleio_gatt_disconnect_complete, + void* callback_context +); + +extern int BLEIO_gatt_read_char_by_uuid( + BLEIO_GATT_HANDLE bleio_gatt_handle, + const char* ble_uuid, + ON_BLEIO_GATT_ATTRIB_READ_COMPLETE on_bleio_gatt_attrib_read_complete, + void* callback_context +); + +extern int BLEIO_gatt_write_char_by_uuid( + BLEIO_GATT_HANDLE bleio_gatt_handle, + const char* ble_uuid, + const unsigned char* buffer, + size_t size, + ON_BLEIO_GATT_ATTRIB_WRITE_COMPLETE on_bleio_gatt_attrib_write_complete, + void* callback_context +); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* BLE_GATT_IO_H */ diff --git a/modules/ble/inc/ble_gatt_io_linux_common.h b/modules/ble/inc/ble_gatt_io_linux_common.h new file mode 100644 index 00000000..81d734af --- /dev/null +++ b/modules/ble/inc/ble_gatt_io_linux_common.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef BLE_GATT_IO_LINUX_COMMON_H +#define BLE_GATT_IO_LINUX_COMMON_H + +#include + +#include +#include + +#include "bluez_device.h" + +#include "gio_async_seq.h" +#include "ble_gatt_io.h" + +typedef struct BLEIO_GATT_HANDLE_DATA_TAG { + BLE_MAC_ADDRESS device_addr; // MAC address of the BLE device. + GDBusConnection* bus; // connection to system d-bus + GDBusObjectManagerClient* object_manager; // d-bus object manager for org.bluez + bluezdevice* device; // the device to do I/O with + BLEIO_GATT_STATE state; // current connection state + uint8_t ble_controller_index; // index of the bluetooth controller to be used + GTree* char_object_path_map; // maps characteristic UUIDs to d-bus object paths +}BLEIO_GATT_HANDLE_DATA; + +#endif // BLE_GATT_IO_LINUX_COMMON_H diff --git a/modules/ble/inc/ble_hl.h b/modules/ble/inc/ble_hl.h new file mode 100644 index 00000000..26c9c2b0 --- /dev/null +++ b/modules/ble/inc/ble_hl.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef BLE_HL_H +#define BLE_HL_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(BLE_HL)(void); + +#ifdef __cplusplus +} +#endif + +#endif /*BLE_HL_H*/ + diff --git a/modules/ble/inc/ble_utils.h b/modules/ble/inc/ble_utils.h new file mode 100644 index 00000000..18bbdddc --- /dev/null +++ b/modules/ble/inc/ble_utils.h @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef BLE_UTILS_H +#define BLE_UTILS_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ble_gatt_io.h" + +extern bool parse_mac_address(const char* mac_address_str, BLE_MAC_ADDRESS* mac_address); + +#ifdef __cplusplus +} +#endif + +#endif /*BLE_UTILS_H*/ + diff --git a/modules/ble/inc/bleio_seq.h b/modules/ble/inc/bleio_seq.h new file mode 100644 index 00000000..5abf1379 --- /dev/null +++ b/modules/ble/inc/bleio_seq.h @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef BLEIO_SEQ_H +#define BLEIO_SEQ_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/buffer_.h" +#include "ble_gatt_io.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct BLEIO_SEQ_HANDLE_DATA_TAG* BLEIO_SEQ_HANDLE; + +#define BLEIO_SEQ_RESULT_VALUES \ + BLEIO_SEQ_ERROR, \ + BLEIO_SEQ_OK +DEFINE_ENUM(BLEIO_SEQ_RESULT, BLEIO_SEQ_RESULT_VALUES); + +#define BLEIO_SEQ_INSTRUCTION_TYPE_VALUES \ + READ_ONCE, \ + READ_PERIODIC, \ + WRITE_ONCE, \ + WRITE_AT_INIT, \ + WRITE_AT_EXIT +DEFINE_ENUM(BLEIO_SEQ_INSTRUCTION_TYPE, BLEIO_SEQ_INSTRUCTION_TYPE_VALUES); + +typedef struct BLEIO_SEQ_INSTRUCTION_TAG +{ + /** + * The type of instruction this is from the BLEIO_SEQ_INSTRUCTION_TYPE enum. + */ + BLEIO_SEQ_INSTRUCTION_TYPE instruction_type; + + /** + * The GATT characteristic to read from/write to. + */ + STRING_HANDLE characteristic_uuid; + + /** + * Context data that should be passed back to the callback that is invoked + * when this instruction completes execution (or for every instance of + * completion in case this is a recurring instruction). + */ + void* context; + + union + { + /** + * If 'instruction_type' is equal to READ_PERIODIC then this + * value indicates the polling interval in milliseconds. + */ + uint32_t interval_in_ms; + + /** + * If 'instruction_type' is equal to WRITE_AT_INIT or WRITE_AT_EXIT + * or WRITE_ONCE then this is the buffer that is to be written. + */ + BUFFER_HANDLE buffer; + }data; +}BLEIO_SEQ_INSTRUCTION; + +/** + * Callback invoked when the sequencer completes a read operation. + */ +typedef void(*ON_BLEIO_SEQ_READ_COMPLETE)( + BLEIO_SEQ_HANDLE bleio_seq_handle, + void* context, + const char* characteristic_uuid, + BLEIO_SEQ_INSTRUCTION_TYPE type, + BLEIO_SEQ_RESULT result, + BUFFER_HANDLE data +); + +/** + * Callback invoked when the sequencer completes a write operation. + */ +typedef void(*ON_BLEIO_SEQ_WRITE_COMPLETE)( + BLEIO_SEQ_HANDLE bleio_seq_handle, + void* context, + const char* characteristic_uuid, + BLEIO_SEQ_INSTRUCTION_TYPE type, + BLEIO_SEQ_RESULT result +); + +/** +* Callback invoked when the sequence has been destroyed. +*/ +typedef void(*ON_BLEIO_SEQ_DESTROY_COMPLETE)(BLEIO_SEQ_HANDLE bleio_seq_handle, void* context); + +extern BLEIO_SEQ_HANDLE BLEIO_Seq_Create( + BLEIO_GATT_HANDLE bleio_gatt_handle, + VECTOR_HANDLE instructions, + ON_BLEIO_SEQ_READ_COMPLETE on_read_complete, + ON_BLEIO_SEQ_WRITE_COMPLETE on_write_complete +); + +extern void BLEIO_Seq_Destroy( + BLEIO_SEQ_HANDLE bleio_seq_handle, + ON_BLEIO_SEQ_DESTROY_COMPLETE on_destroy_complete, + void* context +); + +extern BLEIO_SEQ_RESULT BLEIO_Seq_Run(BLEIO_SEQ_HANDLE bleio_seq_handle); + +extern BLEIO_SEQ_RESULT BLEIO_Seq_AddInstruction( + BLEIO_SEQ_HANDLE bleio_seq_handle, + BLEIO_SEQ_INSTRUCTION* instruction +); + +#ifdef __cplusplus +} +#endif + +#endif // BLEIO_SEQ_H diff --git a/modules/ble/inc/bleio_seq_linux_common.h b/modules/ble/inc/bleio_seq_linux_common.h new file mode 100644 index 00000000..2e9574b3 --- /dev/null +++ b/modules/ble/inc/bleio_seq_linux_common.h @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef BLEIO_SEQ_LINUX_COMMON_H +#define BLEIO_SEQ_LINUX_COMMON_H + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/refcount.h" + +#include "ble_gatt_io.h" +#include "bleio_seq.h" + +#define BLEIO_SEQ_STATE_VALUES \ + BLEIO_SEQ_STATE_IDLE, \ + BLEIO_SEQ_STATE_RUNNING, \ + BLEIO_SEQ_STATE_SHUTTING_DOWN, \ + BLEIO_SEQ_STATE_ERROR + +DEFINE_ENUM(BLEIO_SEQ_STATE, BLEIO_SEQ_STATE_VALUES); + +typedef struct BLEIO_SEQ_HANDLE_DATA_TAG { + BLEIO_GATT_HANDLE bleio_gatt_handle; + VECTOR_HANDLE instructions; + BLEIO_SEQ_STATE state; + ON_BLEIO_SEQ_READ_COMPLETE on_read_complete; + ON_BLEIO_SEQ_WRITE_COMPLETE on_write_complete; + ON_BLEIO_SEQ_DESTROY_COMPLETE on_destroy_complete; + void* destroy_context; +}BLEIO_SEQ_HANDLE_DATA; + +/** + * Internal optional callback invoked when a read/write operation completes. + */ +typedef void(*ON_INTERNAL_IO_COMPLETE)(BLEIO_SEQ_HANDLE_DATA* bleio_seq_handle, BLEIO_SEQ_INSTRUCTION* instruction); + +// BLEIO_SEQ_HANDLE_DATA is a reference counted object. This is so because +// at any given point in time there could be numerous asynchronous I/O +// operations in progress. If the user calls BLEIO_Seq_Destroy while there +// are still outstanding I/O requests, we want to keep the handle data +// alive till all of them complete. +DEFINE_REFCOUNT_TYPE(BLEIO_SEQ_HANDLE_DATA); + +BLEIO_SEQ_RESULT schedule_write(BLEIO_SEQ_HANDLE_DATA* handle_data, BLEIO_SEQ_INSTRUCTION* instruction, ON_INTERNAL_IO_COMPLETE on_internal_read_complete); +BLEIO_SEQ_RESULT schedule_read(BLEIO_SEQ_HANDLE_DATA* handle_data, BLEIO_SEQ_INSTRUCTION* instruction, ON_INTERNAL_IO_COMPLETE on_internal_read_complete); +BLEIO_SEQ_RESULT schedule_periodic(BLEIO_SEQ_HANDLE_DATA* handle_data, BLEIO_SEQ_INSTRUCTION* instruction, ON_INTERNAL_IO_COMPLETE on_internal_read_complete); +void dec_ref_handle(BLEIO_SEQ_HANDLE_DATA* handle_data); + +#endif // BLEIO_SEQ_LINUX_COMMON_H diff --git a/modules/ble/inc/gio_async_seq.h b/modules/ble/inc/gio_async_seq.h new file mode 100644 index 00000000..5b715e86 --- /dev/null +++ b/modules/ble/inc/gio_async_seq.h @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef GIO_ASYNC_SEQ_H +#define GIO_ASYNC_SEQ_H + +#include +#include + +#include "azure_c_shared_utility/macro_utils.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct GIO_ASYNCSEQ_HANDLE_DATA_TAG* GIO_ASYNCSEQ_HANDLE; + +#define GIO_ASYNCSEQ_RESULT_VALUES \ + GIO_ASYNCSEQ_ERROR, \ + GIO_ASYNCSEQ_OK + +DEFINE_ENUM(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_RESULT_VALUES); + +#define GIO_ASYNCSEQ_STATE_VALUES \ + GIO_ASYNCSEQ_STATE_PENDING, \ + GIO_ASYNCSEQ_STATE_RUNNING, \ + GIO_ASYNCSEQ_STATE_COMPLETE, \ + GIO_ASYNCSEQ_STATE_ERROR + +DEFINE_ENUM(GIO_ASYNCSEQ_STATE, GIO_ASYNCSEQ_STATE_VALUES); + +typedef void (*GIO_ASYNCSEQ_CALLBACK)( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +); + +typedef void (*GIO_ASYNCSEQ_ERROR_CALLBACK)( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + const GError* error +); + +typedef gpointer (*GIO_ASYNCSEQ_FINISH_CALLBACK)( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + GAsyncResult* result, + GError** error +); + +typedef void(*GIO_ASYNCSEQ_COMPLETE_CALLBACK)( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result +); + +extern GIO_ASYNCSEQ_HANDLE GIO_Async_Seq_Create( + gpointer async_seq_context, + GIO_ASYNCSEQ_ERROR_CALLBACK error_callback, + GIO_ASYNCSEQ_COMPLETE_CALLBACK complete_callback +); + +extern void GIO_Async_Seq_Destroy(GIO_ASYNCSEQ_HANDLE async_seq_handle); + +extern gpointer GIO_Async_Seq_GetContext(GIO_ASYNCSEQ_HANDLE async_seq_handle); + +/** + * The varargs part of this API is a sequence of parameter pairs, where each + * pair is a GIO_ASYNCSEQ_CALLBACK pointer followed by a GIO_ASYNCSEQ_FINISH_CALLBACK + * pointer. For e.g., + * + * GIO_Async_Seq_Add(async_seq_handle, NULL, + * on_get_bus, on_get_bus_finish, + * on_get_object_manager, on_get_object_manager_finish, + * on_get_device, on_get_device_finish, + * NULL + * ); + */ +extern GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Add( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer callback_context, + ... +); + +extern GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Addv( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer callback_context, + va_list args_list +); + +extern GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Run_Async(GIO_ASYNCSEQ_HANDLE async_seq_handle); + +#ifdef __cplusplus +} +#endif + +#endif // GIO_ASYNC_SEQ_H diff --git a/modules/ble/src/ble.c b/modules/ble/src/ble.c new file mode 100644 index 00000000..8fb03875 --- /dev/null +++ b/modules/ble/src/ble.c @@ -0,0 +1,758 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#if __linux__ +#include +#endif + +#include + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/gb_time.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/threadapi.h" + +#include "module.h" +#include "message.h" +#include "message_bus.h" +#include "ble_gatt_io.h" +#include "bleio_seq.h" +#include "messageproperties.h" +#include "ble_utils.h" +#include "ble.h" + +DEFINE_ENUM_STRINGS(BLEIO_SEQ_INSTRUCTION_TYPE, BLEIO_SEQ_INSTRUCTION_TYPE_VALUES); + +typedef struct BLE_HANDLE_DATA_TAG +{ + MESSAGE_BUS_HANDLE bus; + BLE_DEVICE_CONFIG device_config; + BLEIO_GATT_HANDLE bleio_gatt; + BLEIO_SEQ_HANDLE bleio_seq; + bool is_connected; + bool is_destroy_complete; +#if __linux__ + GMainLoop* main_loop; + THREAD_HANDLE event_thread; +#endif +}BLE_HANDLE_DATA; + +// how long to wait for a destroy complete callback to be invoked +// in microseconds +#define DESTROY_COMPLETE_TIMEOUT (1000000 * 5) + +static void on_connect_complete( + BLEIO_GATT_HANDLE bleio_gatt_handle, + void* context, + BLEIO_GATT_CONNECT_RESULT connect_result +); + +static void on_disconnect_complete( + BLEIO_GATT_HANDLE bleio_gatt_handle, + void* context +); + +static void on_read_complete( + BLEIO_SEQ_HANDLE bleio_seq_handle, + void* context, + const char* characteristic_uuid, + BLEIO_SEQ_INSTRUCTION_TYPE type, + BLEIO_SEQ_RESULT result, + BUFFER_HANDLE data +); + +static void on_write_complete( + BLEIO_SEQ_HANDLE bleio_seq_handle, + void* context, + const char* characteristic_uuid, + BLEIO_SEQ_INSTRUCTION_TYPE type, + BLEIO_SEQ_RESULT result +); + +static VECTOR_HANDLE ble_instr_to_bleioseq_instr( + BLE_HANDLE_DATA* module, + VECTOR_HANDLE source_instructions +); + +#if __linux__ +// how long to wait for a event dispatcher thread to start up +#define EVENT_DISPATCHER_START_TIMEOUT (G_USEC_PER_SEC * 5) + +static bool init_glib_loop( + BLE_HANDLE_DATA* handle_data +); + +static int event_dispatcher( + void * user_data +); + +static bool terminate_event_dispatcher( + BLE_HANDLE_DATA* handle_data +); +#endif + +static MODULE_HANDLE BLE_Create(MESSAGE_BUS_HANDLE bus, const void* configuration) +{ + BLE_HANDLE_DATA* result; + BLE_CONFIG* config = (BLE_CONFIG*)configuration; + + /*Codes_SRS_BLE_13_001: [BLE_Create shall return NULL if bus is NULL.]*/ + /*Codes_SRS_BLE_13_002: [BLE_Create shall return NULL if configuration is NULL.]*/ + /*Codes_SRS_BLE_13_003: [BLE_Create shall return NULL if configuration->instructions is NULL.]*/ + /*Codes_SRS_BLE_13_004: [BLE_Create shall return NULL if the configuration->instructions vector is empty(size is zero).]*/ + if ( + bus == NULL || + configuration == NULL || + config->instructions == NULL || + VECTOR_size(config->instructions) == 0 + ) + { + LogError("Invalid input"); + result = NULL; + } + else + { + /*Codes_SRS_BLE_13_009: [ BLE_Create shall allocate memory for an instance of the BLE_HANDLE_DATA structure and use that as the backing structure for the module handle. ]*/ + result = (BLE_HANDLE_DATA*)malloc(sizeof(BLE_HANDLE_DATA)); + if (result == NULL) + { + /*Codes_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + LogError("malloc failed"); + /* result is already NULL */ + } + else + { + /*Codes_SRS_BLE_13_008: [ BLE_Create shall create and initialize the bleio_gatt field in the BLE_HANDLE_DATA object by calling BLEIO_gatt_create . ]*/ + result->bleio_gatt = BLEIO_gatt_create(&(config->device_config)); + if (result->bleio_gatt == NULL) + { + /*Codes_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + LogError("BLEIO_gatt_create failed"); + free(result); + result = NULL; + } + else + { + // transform BLE_INSTRUCTION objects into BLEIO_SEQ_INSTRUCTION objects + VECTOR_HANDLE instructions = ble_instr_to_bleioseq_instr(result, config->instructions); + if (instructions == NULL) + { + /*Codes_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + LogError("Converting BLE_INSTRUCTION objects into BLEIO_SEQ_INSTRUCTION objects failed"); + BLEIO_gatt_destroy(result->bleio_gatt); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_BLE_13_010: [ BLE_Create shall create and initialize the bleio_seq field in the BLE_HANDLE_DATA object by calling BLEIO_Seq_Create . ]*/ + result->bleio_seq = BLEIO_Seq_Create( + result->bleio_gatt, + instructions, + on_read_complete, + on_write_complete + ); + if (result->bleio_seq == NULL) + { + /*Codes_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + LogError("BLEIO_Seq_Create failed"); + BLEIO_gatt_destroy(result->bleio_gatt); + VECTOR_destroy(instructions); + free(result); + result = NULL; + } + else + { + result->bus = bus; + memcpy(&(result->device_config), &(config->device_config), sizeof(result->device_config)); + result->is_destroy_complete = false; + +#if __linux__ + if (init_glib_loop(result) == false) + { + LogError("init_glib_loop returned false"); + BLEIO_Seq_Destroy(result->bleio_seq, NULL, NULL); + free(result); + result = NULL; + } + else + { +#endif + /*Codes_SRS_BLE_13_011: [ BLE_Create shall asynchronously open a connection to the BLE device by calling BLEIO_gatt_connect . ]*/ + result->is_connected = false; + if (BLEIO_gatt_connect(result->bleio_gatt, on_connect_complete, (void*)result) != 0) + { + /*Codes_SRS_BLE_13_012: [ BLE_Create shall return NULL if BLEIO_gatt_connect returns a non-zero value. ]*/ + LogError("BLEIO_gatt_connect failed"); +#if __linux__ + if (terminate_event_dispatcher(result) == false) + { + LogError("terminate_event_dispatcher returned false"); + } +#endif + BLEIO_Seq_Destroy(result->bleio_seq, NULL, NULL); + free(result); + result = NULL; + + /** + * We don't destroy handle_data->bleio_gatt because the handle is + * 'owned' by the BLEIO sequence object and that will destroy the + * GATT I/O object. + */ + } +#if __linux__ + } +#endif + } + } + } + } + } + + /*Codes_SRS_BLE_13_006: [ BLE_Create shall return a non- NULL MODULE_HANDLE when successful. ]*/ + return (MODULE_HANDLE)result; +} + +#if __linux__ +static bool init_glib_loop(BLE_HANDLE_DATA* handle_data) +{ + bool result; + handle_data->main_loop = g_main_loop_new(NULL, FALSE); + if (handle_data->main_loop == NULL) + { + LogError("g_main_loop_new returned NULL"); + result = false; + } + else + { + // start a thread to pump the message loop + if (ThreadAPI_Create( + &(handle_data->event_thread), + event_dispatcher, + (void*)handle_data + ) != THREADAPI_OK) + { + LogError("ThreadAPI_Create failed"); + g_main_loop_unref(handle_data->main_loop); + result = false; + } + else + { + result = true; + } + } + + return result; +} + +static int event_dispatcher(void * user_data) +{ + BLE_HANDLE_DATA* handle_data = (BLE_HANDLE_DATA*)user_data; + g_main_loop_run(handle_data->main_loop); + g_main_loop_unref(handle_data->main_loop); + return 0; +} + +static bool terminate_event_dispatcher(BLE_HANDLE_DATA* handle_data) +{ + bool result; + gint64 start_time = g_get_monotonic_time(); + if (handle_data->main_loop != NULL) + { + GMainContext* loop_context = g_main_loop_get_context(handle_data->main_loop); + if (loop_context != NULL) + { + while ( + (g_get_monotonic_time() - start_time) < EVENT_DISPATCHER_START_TIMEOUT + && + g_main_loop_is_running(handle_data->main_loop) == FALSE + ) + { + // wait for quarter of a second + g_usleep(G_USEC_PER_SEC / 4); + } + + if (g_main_loop_is_running(handle_data->main_loop) == TRUE) + { + g_main_loop_quit(handle_data->main_loop); + result = true; + } + else + { + LogError("Timed out waiting for event dispatcher thread to initialize."); + result = false; + } + } + else + { + LogError("g_main_loop_get_context returned NULL"); + result = false; + } + } + else + { + LogError("No GLIB loop to terminate."); + result = false; + } + + return result; +} +#endif + +static VECTOR_HANDLE ble_instr_to_bleioseq_instr(BLE_HANDLE_DATA* module, VECTOR_HANDLE source_instructions) +{ + VECTOR_HANDLE result = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + if (result != NULL) + { + size_t i, len = VECTOR_size(source_instructions); + for (i = 0; i < len; ++i) + { + BLE_INSTRUCTION* src_instr = (BLE_INSTRUCTION*)VECTOR_element(source_instructions, i); + + // copy the data + BLEIO_SEQ_INSTRUCTION instr; + instr.instruction_type = src_instr->instruction_type; + instr.characteristic_uuid = src_instr->characteristic_uuid; + memcpy(&(instr.data), &(src_instr->data), sizeof(instr.data)); + instr.context = (void*)module; + + if (VECTOR_push_back(result, &instr, 1) != 0) + { + LogError("VECTOR_push_back failed"); + break; + } + } + + // if the loop terminated before all elements were processed, + // then something went wrong + if (i < len) + { + VECTOR_destroy(result); + result = NULL; + } + } + else + { + LogError("VECTOR_create failed"); + } + + return result; +} + +static void on_connect_complete( + BLEIO_GATT_HANDLE bleio_gatt_handle, + void* context, + BLEIO_GATT_CONNECT_RESULT connect_result +) +{ + // this MUST NOT be NULL + BLE_HANDLE_DATA* handle_data = (BLE_HANDLE_DATA*)context; + if (connect_result != BLEIO_GATT_CONNECT_OK) + { + LogError("BLEIO_gatt_connect asynchronously failed"); + } + else + { + handle_data->is_connected = true; + + /*Codes_SRS_BLE_13_014: [ If the asynchronous call to BLEIO_gatt_connect is successful then the BLEIO_Seq_Run function shall be called on the bleio_seq field from BLE_HANDLE_DATA . ]*/ + if (BLEIO_Seq_Run(handle_data->bleio_seq) != BLEIO_SEQ_OK) + { + LogError("BLEIO_Seq_Run failed"); + } + } +} + +static int format_timestamp(char* dest, size_t dest_size) +{ + int result; + time_t t1 = time(NULL); + if (t1 == (time_t)-1) + { + LogError("time() failed"); + result = __LINE__; + } + else + { + struct tm* t2 = localtime(&t1); + if (t2 == NULL) + { + LogError("localtime() failed"); + result = __LINE__; + } + else + { + /** + * Note: We record the time only with a granularity of seconds. We + * may want to increase this to include milliseconds. + */ + if (strftime(dest, dest_size, "%Y:%m:%dT%H:%M:%S", t2) == 0) + { + LogError("strftime() failed"); + result = __LINE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +static void on_read_complete( + BLEIO_SEQ_HANDLE bleio_seq_handle, + void* context, + const char* characteristic_uuid, + BLEIO_SEQ_INSTRUCTION_TYPE type, + BLEIO_SEQ_RESULT result, + BUFFER_HANDLE data +) +{ + // this MUST NOT be NULL + BLE_HANDLE_DATA* handle_data = (BLE_HANDLE_DATA*)context; + if (result != BLEIO_SEQ_OK) + { + LogError("A read instruction for characteristic %s of type %s failed.", + characteristic_uuid, ENUM_TO_STRING(BLEIO_SEQ_INSTRUCTION_TYPE, type)); + } + else + { + MAP_HANDLE message_properties = Map_Create(NULL); + if (message_properties == NULL) + { + LogError("Map_Create() failed"); + } + else + { + // format BLE controller index + char ble_controller_index[80]; + int ret = snprintf(ble_controller_index, + sizeof(ble_controller_index) / sizeof(ble_controller_index[0]), + "%d", + handle_data->device_config.ble_controller_index + ); + if(ret < 0 || ret >= (sizeof(ble_controller_index) / sizeof(ble_controller_index[0]))) + { + LogError("snprintf() failed"); + } + else + { + // format MAC address + char mac_address[18] = ""; + ret = snprintf( + mac_address, + sizeof(mac_address) / sizeof(mac_address[0]), + "%02X:%02X:%02X:%02X:%02X:%02X", + handle_data->device_config.device_addr.address[0], + handle_data->device_config.device_addr.address[1], + handle_data->device_config.device_addr.address[2], + handle_data->device_config.device_addr.address[3], + handle_data->device_config.device_addr.address[4], + handle_data->device_config.device_addr.address[5] + ); + if (ret < 0 || ret >= (sizeof(mac_address) / sizeof(mac_address[0]))) + { + LogError("snprintf() failed"); + } + else + { + // format timestamp + char timestamp[25] = ""; + if (format_timestamp(timestamp, sizeof(timestamp) / sizeof(timestamp[0])) != 0) + { + LogError("format_timestamp() failed"); + } + else if (Map_Add(message_properties, GW_BLE_CONTROLLER_INDEX_PROPERTY, ble_controller_index) != MAP_OK) + { + LogError("Map_Add() failed for property %s", GW_BLE_CONTROLLER_INDEX_PROPERTY); + } + else if (Map_Add(message_properties, GW_MAC_ADDRESS_PROPERTY, mac_address) != MAP_OK) + { + LogError("Map_Add() failed for property %s", GW_MAC_ADDRESS_PROPERTY); + } + else if (Map_Add(message_properties, GW_TIMESTAMP_PROPERTY, timestamp) != MAP_OK) + { + LogError("Map_Add() failed for property %s", GW_TIMESTAMP_PROPERTY); + } + else if (Map_Add(message_properties, GW_CHARACTERISTIC_UUID_PROPERTY, characteristic_uuid) != MAP_OK) + { + LogError("Map_Add() failed for property %s", GW_CHARACTERISTIC_UUID_PROPERTY); + } + else if (Map_Add(message_properties, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_TELEMETRY) != MAP_OK) + { + LogError("Map_Add() failed for property %s", GW_SOURCE_PROPERTY); + } + else + { + MESSAGE_CONFIG message_config; + message_config.sourceProperties = message_properties; + message_config.size = BUFFER_length(data); // "data" MUST NOT be NULL here + message_config.source = (const unsigned char*)BUFFER_u_char(data); + + MESSAGE_HANDLE message = Message_Create(&message_config); + if (message == NULL) + { + LogError("Message_Create() failed"); + } + else + { + /*Codes_SRS_BLE_13_019: [BLE_Create shall handle the ON_BLEIO_SEQ_READ_COMPLETE callback on the BLE I/O sequence. If the call is successful then a new message shall be published on the bus with the buffer that was read as the content of the message along with the following properties: + + | Property Name | Description | + |-------------------------|---------------------------------------------------------------| + | ble_controller_index | The index of the bluetooth radio hardware on the device. | + | mac_address | MAC address of the BLE device from which the data was read. | + | timestamp | Timestamp indicating when the data was read. | + ]*/ + if (MessageBus_Publish(handle_data->bus, message) != MESSAGE_BUS_OK) + { + LogError("MessageBus_Publish() failed"); + } + + Message_Destroy(message); + } + } + + Map_Destroy(message_properties); + } + } + } + } + + BUFFER_delete(data); +} + +void on_write_complete( + BLEIO_SEQ_HANDLE bleio_seq_handle, + void * context, + const char * characteristic_uuid, + BLEIO_SEQ_INSTRUCTION_TYPE type, + BLEIO_SEQ_RESULT result +) +{ + // this MUST NOT be NULL + BLE_HANDLE_DATA* handle_data = (BLE_HANDLE_DATA*)context; + + if (result != BLEIO_SEQ_OK) + { + // format MAC address + char mac_address[18]; + snprintf( + mac_address, + sizeof(mac_address) / sizeof(mac_address[0]), + "%02X:%02X:%02X:%02X:%02X:%02X", + handle_data->device_config.device_addr.address[0], + handle_data->device_config.device_addr.address[1], + handle_data->device_config.device_addr.address[2], + handle_data->device_config.device_addr.address[3], + handle_data->device_config.device_addr.address[4], + handle_data->device_config.device_addr.address[5] + ); + + LogError( + "Write instruction of type %s for device %s on BLE controller %d for characteristic %s failed", + ENUM_TO_STRING(BLEIO_SEQ_INSTRUCTION_TYPE, type), + mac_address, + handle_data->device_config.ble_controller_index, + characteristic_uuid + ); + } +} + +static bool is_message_for_module(const char* mac_address_str, BLE_HANDLE_DATA* handle_data) +{ + bool result; + BLE_MAC_ADDRESS mac_address; + + result = parse_mac_address(mac_address_str, &mac_address); + if (result == true) + { + /*Codes_SRS_BLE_13_022: [ BLE_Receive shall ignore the message unless the 'macAddress' property matches the MAC address that was passed to this module when it was created. ]*/ + // check if the mac address matches this module's mac address + if (memcmp(&handle_data->device_config.device_addr, &mac_address, sizeof(BLE_MAC_ADDRESS)) == 0) + { + result = true; + } + else + { + result = false; + } + } + else + { + LogError("Invalid mac address"); + } + + return result; +} + +static void BLE_Receive(MODULE_HANDLE module, MESSAGE_HANDLE message) +{ + /*Codes_SRS_BLE_13_018: [ BLE_Receive shall do nothing if module is NULL or if message is NULL. ]*/ + if (module != NULL && message != NULL) + { + BLE_HANDLE_DATA* handle_data = (BLE_HANDLE_DATA*)module; + CONSTMAP_HANDLE properties = Message_GetProperties(message); + + /*Codes_SRS_BLE_13_020: [ BLE_Receive shall ignore all messages except those that have the following properties: + >| Property Name | Description | + >|-------------------------|-------------------------------------------------------------------------| + >| source | This property should have the value "BLE". | + >| macAddress | MAC address of the BLE device to which the data to should be written. | + ]*/ + // fetch the 'source' property + const char* source = ConstMap_GetValue(properties, GW_SOURCE_PROPERTY); + if (source != NULL && strcmp(source, GW_SOURCE_BLE_COMMAND) == 0) + { + // fetch the 'macAddress' property + const char* mac_address = ConstMap_GetValue(properties, GW_MAC_ADDRESS_PROPERTY); + if (mac_address != NULL && is_message_for_module(mac_address, handle_data) == true) + { + const CONSTBUFFER* content = Message_GetContent(message); + if (content != NULL && content->buffer != NULL && content->size > 0) + { + BLE_INSTRUCTION* ble_instruction = (BLE_INSTRUCTION*)content->buffer; + + // transform BLE_INSTRUCTION into BLEIO_SEQ_INSTRUCTION + BLEIO_SEQ_INSTRUCTION ble_seq_instruction; + ble_seq_instruction.instruction_type = ble_instruction->instruction_type; + ble_seq_instruction.characteristic_uuid = ble_instruction->characteristic_uuid; + memcpy(&(ble_seq_instruction.data), &(ble_instruction->data), sizeof(ble_instruction->data)); + + // MUST set this as the context so on_read_complete and on_write_complete get + // access to BLE_HANDLE_DATA + ble_seq_instruction.context = (void*)module; + + /*Codes_SRS_BLE_13_021: [ BLE_Receive shall treat the content of the message as a BLE_INSTRUCTION and schedule it for execution by calling BLEIO_Seq_AddInstruction. ]*/ + if (BLEIO_Seq_AddInstruction(handle_data->bleio_seq, &ble_seq_instruction) != BLEIO_SEQ_OK) + { + LogError("BLEIO_Seq_AddInstruction failed"); + } + } + } + } + + ConstMap_Destroy(properties); + } + else + { + LogError("module and/or message is NULL"); + } +} + +static void on_destroy_complete(BLEIO_SEQ_HANDLE bleio_seq_handle, void* context) +{ + BLE_HANDLE_DATA* handle_data = (BLE_HANDLE_DATA*)context; + if (handle_data != NULL) + { + handle_data->is_destroy_complete = true; + + if (handle_data->bleio_gatt != NULL) + { + // if the device is connected then first disconnect + if (handle_data->is_connected) + { + BLEIO_gatt_disconnect(handle_data->bleio_gatt, NULL, NULL); + } + + /** + * We don't destroy handle_data->bleio_gatt because the handle is + * 'owned' by the BLEIO sequence object and that will destroy the + * GATT I/O object. + */ + } + } + else + { + LogError("bleio_seq_handle is NULL"); + } +} + +static void BLE_Destroy(MODULE_HANDLE module) +{ + /*Codes_SRS_BLE_13_016: [If module is NULL BLE_Destroy shall do nothing.]*/ + if (module != NULL) + { + /*Codes_SRS_BLE_13_017: BLE_Destroy shall free all resources. ]*/ + BLE_HANDLE_DATA* handle_data = (BLE_HANDLE_DATA*)module; + if (handle_data->bleio_seq != NULL) + { + BLEIO_Seq_Destroy + ( + handle_data->bleio_seq, + on_destroy_complete, + (void*)handle_data + ); + #if __linux__ + // wait for on_destroy_complete to be called; bail after 5 seconds + gint64 start_time = g_get_monotonic_time(); + if (handle_data->main_loop != NULL) + { + GMainContext* loop_context = g_main_loop_get_context(handle_data->main_loop); + if (loop_context != NULL) + { + LogInfo("Waiting for sequence to be destroyed..."); + while (handle_data->is_destroy_complete == false) + { + g_main_context_iteration(loop_context, FALSE); + if ((g_get_monotonic_time() - start_time) >= DESTROY_COMPLETE_TIMEOUT) + { + LogError("on_destroy_complete did not get called in time"); + break; + } + } + LogInfo("Done waiting for sequence to be destroyed."); + } + else + { + LogError("g_main_loop_get_context returned NULL"); + } + + // terminate glib loop + g_main_loop_quit(handle_data->main_loop); + + // wait for thread to exit + int thread_result; + if (ThreadAPI_Join(handle_data->event_thread, &thread_result) != THREADAPI_OK) + { + LogError("ThreadAPI_Join() returned an error"); + } + } +#endif + } + + free(handle_data); + } + else + { + LogError("module handle is NULL"); + } +} + +static const MODULE_APIS Module_GetAPIS_Impl = +{ + BLE_Create, + BLE_Destroy, + BLE_Receive +}; + +/*Codes_SRS_BLE_13_007: [Module_GetAPIS shall return a non - NULL pointer to a structure of type MODULE_APIS that has all fields initialized to non - NULL values.]*/ +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(BLE_MODULE)(void) +#else +const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + return &Module_GetAPIS_Impl; +} diff --git a/modules/ble/src/ble_gatt_io_linux.c b/modules/ble/src/ble_gatt_io_linux.c new file mode 100644 index 00000000..29a4ab06 --- /dev/null +++ b/modules/ble/src/ble_gatt_io_linux.c @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include +#include + +#include "bluez_device.h" +#include "bluez_characteristic.h" + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" + +#include "gio_async_seq.h" +#include "ble_gatt_io.h" +#include "ble_gatt_io_linux_common.h" + +static gint g_string_cmp(gconstpointer s1, gconstpointer s2, gpointer user_data); +static void tree_key_value_destroy(gpointer data); + +BLEIO_GATT_HANDLE BLEIO_gatt_create( + const BLE_DEVICE_CONFIG* config +) +{ + BLEIO_GATT_HANDLE_DATA* result; + if (config != NULL) + { + result = malloc(sizeof(BLEIO_GATT_HANDLE_DATA)); + if (result != NULL) + { + // create a map that maps characteristic UUIDs to + // d-bus object maps; both the key and value are + // expected to be GString strings + result->char_object_path_map = g_tree_new_full( + g_string_cmp, + NULL, + tree_key_value_destroy, + tree_key_value_destroy + ); + if(result->char_object_path_map != NULL) + { + // save the device's MAC address + memcpy( + &(result->device_addr), + &(config->device_addr), + sizeof(BLE_MAC_ADDRESS) + ); + result->bus = NULL; + result->object_manager = NULL; + result->device = NULL; + result->state = BLEIO_GATT_STATE_DISCONNECTED; + result->ble_controller_index = config->ble_controller_index; + + /*Codes_SRS_BLEIO_GATT_13_001: [ BLEIO_gatt_create shall return a non-NULL handle on successful execution. ]*/ + } + else + { + LogError("g_tree_new failed"); + free(result); + result = NULL; + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_002: [ BLEIO_gatt_create shall return NULL when any of the underlying platform calls fail. ]*/ + LogError("malloc failed"); + /* Fall through. 'result' is NULL. */ + } + } + else + { + /* Codes_SRS_BLEIO_GATT_13_003: [ BLEIO_gatt_create shall return NULL if config is NULL. ] */ + LogError("config is NULL"); + result = NULL; + } + + return (BLEIO_GATT_HANDLE)result; +} + +void BLEIO_gatt_destroy( + BLEIO_GATT_HANDLE bleio_gatt_handle +) +{ + /*Codes_SRS_BLEIO_GATT_13_005: [ If bleio_gatt_handle is NULL BLEIO_gatt_destroy shall do nothing. ]*/ + if (bleio_gatt_handle != NULL) + { + /*Codes_SRS_BLEIO_GATT_13_004: [ BLEIO_gatt_destroy shall free all resources associated with the handle. ]*/ + BLEIO_GATT_HANDLE_DATA* handle_data = (BLEIO_GATT_HANDLE_DATA*)bleio_gatt_handle; + + if (handle_data->bus != NULL) + { + g_object_unref(handle_data->bus); + } + if (handle_data->object_manager != NULL) + { + g_object_unref(handle_data->object_manager); + } + if (handle_data->device != NULL) + { + g_object_unref(handle_data->device); + } + if (handle_data->char_object_path_map != NULL) + { + g_tree_unref(handle_data->char_object_path_map); + } + + free(handle_data); + } +} + +static gint g_string_cmp(gconstpointer s1, gconstpointer s2, gpointer user_data) +{ + (void)user_data; + + gint result; + + if (s1 == NULL && s2 != NULL) + { + result = -1; + } + else if (s2 == NULL && s1 != NULL) + { + result = 1; + } + else if (s1 == NULL && s2 == NULL) + { + result = 0; + } + else + { + GString* gs1 = (GString*)s1; + GString* gs2 = (GString*)s2; + + result = g_ascii_strncasecmp + ( + gs1->str, + gs2->str, + gs1->len < gs2->len ? gs1->len : gs2->len + ); + } + + return result; +} + +static void tree_key_value_destroy(gpointer data) +{ + if (data != NULL) + { + g_string_free((GString*)data, TRUE); + } +} diff --git a/modules/ble/src/ble_gatt_io_linux_connect.c b/modules/ble/src/ble_gatt_io_linux_connect.c new file mode 100644 index 00000000..fd058199 --- /dev/null +++ b/modules/ble/src/ble_gatt_io_linux_connect.c @@ -0,0 +1,473 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include +#include + +#include "bluez_device.h" +#include "bluez_characteristic.h" + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" + +#include "gio_async_seq.h" +#include "ble_gatt_io.h" +#include "ble_gatt_io_linux_common.h" + +typedef struct CONNECT_CONTEXT_TAG +{ + BLEIO_GATT_HANDLE_DATA* handle_data; + GString* device_path; + GIO_ASYNCSEQ_HANDLE async_seq; + ON_BLEIO_GATT_CONNECT_COMPLETE on_connect_complete; + void* callback_context; +}CONNECT_CONTEXT; + +// called when an error occurs in an async call +static void on_sequence_error(GIO_ASYNCSEQ_HANDLE async_seq_handle, const GError* error); + +// called when the entire async sequence completes +static void on_sequence_complete(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result); + +// async sequence functions +static void connect_system_bus(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result, gpointer callback_context, GAsyncReadyCallback async_callback); +static gpointer connect_system_bus_finish(GIO_ASYNCSEQ_HANDLE async_seq_handle, GAsyncResult* result, GError** error); + +static void create_object_manager(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result, gpointer callback_context, GAsyncReadyCallback async_callback); +static gpointer create_object_manager_finish(GIO_ASYNCSEQ_HANDLE async_seq_handle, GAsyncResult* result, GError** error); + +static void create_device_proxy(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result, gpointer callback_context, GAsyncReadyCallback async_callback); +static gpointer create_device_proxy_finish(GIO_ASYNCSEQ_HANDLE async_seq_handle, GAsyncResult* result, GError** error); + +static void connect_device(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result, gpointer callback_context, GAsyncReadyCallback async_callback); +static gpointer connect_device_finish(GIO_ASYNCSEQ_HANDLE async_seq_handle, GAsyncResult* result, GError** error); + +// utilities +static void free_context(CONNECT_CONTEXT* context); + +// The connect API essentially performs the following asynchronous +// operations in sequence. There has to be an active GLIB loop +// running in order for all this to work. +// +// [*] Get a connection to the system d-bus +// [*] Get a reference to the d-bus object manager for the +// org.bluez root object +// [*] Get a device proxy object for the given mac address +// [*] Open a connection to the device +// [*] Load up all characteristics on the device and map +// d-bus object paths to GATT characteristic UUIDs + +int BLEIO_gatt_connect( + BLEIO_GATT_HANDLE bleio_gatt_handle, + ON_BLEIO_GATT_CONNECT_COMPLETE on_bleio_gatt_connect_complete, + void* callback_context +) +{ + int result; + BLEIO_GATT_HANDLE_DATA* handle_data = (BLEIO_GATT_HANDLE_DATA*)bleio_gatt_handle; + + /*Codes_SRS_BLEIO_GATT_13_047: [ If when BLEIO_gatt_connect is called, there's another connection request already in progress or if an open connection already exists, then this API shall return a non-zero error code. ]*/ + if ( + bleio_gatt_handle != NULL && + on_bleio_gatt_connect_complete != NULL && + ( + handle_data->state == BLEIO_GATT_STATE_DISCONNECTED || + handle_data->state == BLEIO_GATT_STATE_ERROR + ) + ) + { + CONNECT_CONTEXT *context = (CONNECT_CONTEXT *)malloc(sizeof(CONNECT_CONTEXT)); + if (context == NULL) + { + /*Codes_SRS_BLEIO_GATT_13_009: [ If any of the underlying platform calls fail, BLEIO_gatt_connect shall return a non-zero value. ]*/ + result = __LINE__; + LogError("malloc failed."); + } + else + { + // create async sequence + context->async_seq = GIO_Async_Seq_Create( + context, + on_sequence_error, + on_sequence_complete + ); + if (context->async_seq != NULL) + { + /*Codes_SRS_BLEIO_GATT_13_007: [ BLEIO_gatt_connect shall asynchronously attempt to open a connection with the BLE device. ]*/ + // setup call sequence + GIO_ASYNCSEQ_RESULT seq_result = GIO_Async_Seq_Add( + context->async_seq, NULL, + + // connect to the system bus + connect_system_bus, connect_system_bus_finish, + + // create an instance of the d-bus object manager + create_object_manager, create_object_manager_finish, + + // create an instance of a device proxy object + create_device_proxy, create_device_proxy_finish, + + // connect to the device + connect_device, connect_device_finish, + + // sentinel value to signal end of sequence + NULL + ); + if (seq_result == GIO_ASYNCSEQ_OK) + { + context->device_path = NULL; + context->handle_data = handle_data; + context->on_connect_complete = on_bleio_gatt_connect_complete; + + /*Codes_SRS_BLEIO_GATT_13_012: [ When on_bleio_gatt_connect_complete is invoked the value passed in callback_context to BLEIO_gatt_connect shall be passed along to on_bleio_gatt_connect_complete. ]*/ + context->callback_context = callback_context; + + // kick-off the sequence + handle_data->state = BLEIO_GATT_STATE_CONNECTING; + if(GIO_Async_Seq_Run_Async(context->async_seq) == GIO_ASYNCSEQ_OK) + { + /*Codes_SRS_BLEIO_GATT_13_008: [ On initiating the connect successfully, BLEIO_gatt_connect shall return 0 (zero). ]*/ + result = 0; + } + else + { + result = __LINE__; + GIO_Async_Seq_Destroy(context->async_seq); + free(context); + LogError("GIO_Async_Seq_Run failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_009: [ If any of the underlying platform calls fail, BLEIO_gatt_connect shall return a non-zero value. ]*/ + result = __LINE__; + GIO_Async_Seq_Destroy(context->async_seq); + free(context); + LogError("GIO_Async_Seq_Add failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_009: [ If any of the underlying platform calls fail, BLEIO_gatt_connect shall return a non-zero value. ]*/ + result = __LINE__; + free(context); + LogError("GIO_Async_Seq_Create failed."); + } + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_010: [ If bleio_gatt_handle or on_bleio_gatt_connect_complete are NULL then BLEIO_gatt_connect shall return a non-zero value. ]*/ + result = __LINE__; + LogError("Invalid args or the state of the object is unexpected."); + } + + return result; +} + +static void connect_system_bus( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +) +{ + g_bus_get( + G_BUS_TYPE_SYSTEM, + NULL, // not cancellable + async_callback, + async_seq_handle + ); +} + +static gpointer connect_system_bus_finish( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + GAsyncResult* result, + GError** error +) +{ + return g_bus_get_finish(result, error); +} + +static void create_object_manager( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +) +{ + CONNECT_CONTEXT* context = (CONNECT_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + context->handle_data->bus = (GDBusConnection*)previous_result; + + g_dbus_object_manager_client_new( + context->handle_data->bus, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + "org.bluez", + "/", + NULL, NULL, NULL, // no custom proxies + NULL, // not cancellable + async_callback, + async_seq_handle + ); +} + +static gpointer create_object_manager_finish( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + GAsyncResult* result, + GError** error +) +{ + return g_dbus_object_manager_client_new_finish(result, error); +} + +static void create_device_proxy( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +) +{ + CONNECT_CONTEXT* context = (CONNECT_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + context->handle_data->object_manager = (GDBusObjectManagerClient*)previous_result; + + // convert BLE MAC address to an object path + context->device_path = g_string_new(NULL); + g_string_printf( + context->device_path, + "/org/bluez/hci%d/dev_%02X_%02X_%02X_%02X_%02X_%02X", + context->handle_data->ble_controller_index, + context->handle_data->device_addr.address[0], + context->handle_data->device_addr.address[1], + context->handle_data->device_addr.address[2], + context->handle_data->device_addr.address[3], + context->handle_data->device_addr.address[4], + context->handle_data->device_addr.address[5] + ); + + bluez_device__proxy_new( + context->handle_data->bus, + G_DBUS_PROXY_FLAGS_NONE, + "org.bluez", + context->device_path->str, + NULL, // not cancellable + async_callback, + async_seq_handle + ); +} + +static gpointer create_device_proxy_finish( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + GAsyncResult* result, + GError** error +) +{ + return bluez_device__proxy_new_finish(result, error); +} + +static void connect_device( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +) +{ + CONNECT_CONTEXT* context = (CONNECT_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + context->handle_data->device = (bluezdevice*)previous_result; + + /*Codes_SRS_BLEIO_GATT_13_007: [ BLEIO_gatt_connect shall asynchronously attempt to open a connection with the BLE device. ]*/ + bluez_device__call_connect( + context->handle_data->device, + NULL, + async_callback, + async_seq_handle + ); +} + +static gpointer connect_device_finish( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + GAsyncResult* result, + GError** error +) +{ + CONNECT_CONTEXT* context = (CONNECT_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + return (gpointer)(uintptr_t)bluez_device__call_connect_finish( + context->handle_data->device, result, error + ); +} + +static gint is_interface_type(gconstpointer data, gconstpointer user_data) +{ + GDBusProxy* proxy = (GDBusProxy*)data; + const gchar* interface_name = g_dbus_proxy_get_interface_name(proxy); + return g_strcmp0(interface_name, (const gchar*)user_data); +} + +static void unref_object(gpointer data) +{ + g_object_unref(data); +} + +static void unref_list_object(gpointer data, gpointer user_data) +{ + unref_object(data); +} + +static GString* get_characteristic_uuid(GDBusObject* object) +{ + // 'object' is a GATT characteristic if one of the interfaces + // that this object supports is 'org.bluez.GattCharacteristic1' + + GString* result; + GList* interfaces = g_dbus_object_get_interfaces(object); + if (interfaces != NULL) + { + GList* characteristic_item = g_list_find_custom( + interfaces, + (gconstpointer)"org.bluez.GattCharacteristic1", + is_interface_type + ); + if (characteristic_item != NULL) + { + GVariant* var = g_dbus_proxy_get_cached_property( + (GDBusProxy*)(characteristic_item->data), + "UUID" + ); + if (var != NULL) + { + result = g_string_new(g_variant_get_string(var, NULL)); + g_variant_unref(var); + } + else + { + result = NULL; + } + } + else + { + result = NULL; + } + + g_list_foreach(interfaces, unref_list_object, NULL); + g_list_free(interfaces); + } + else + { + result = NULL; + } + + return result; +} + +static void process_object(gpointer data, gpointer user_data) +{ + CONNECT_CONTEXT* context = (CONNECT_CONTEXT*)user_data; + GDBusObject* object = (GDBusObject*)data; + + // get device and object paths + const gchar* device_path = g_dbus_proxy_get_object_path( + (GDBusProxy*)context->handle_data->device + ); + const gchar* object_path = g_dbus_object_get_object_path(object); + + // if "device_path" is a prefix of "object_path" then + // "object" is a child of "device" + if ( + device_path != NULL && + object_path != NULL && + g_strcmp0(object_path, device_path) != 0 && + g_str_has_prefix(object_path, device_path) == TRUE + ) + { + // add UUID of object to map if this is a characteristic + GString* uuid = get_characteristic_uuid(object); + if(uuid != NULL) + { + GString* opath = g_string_new(object_path); + if(opath != NULL) + { + g_tree_insert( + context->handle_data->char_object_path_map, + uuid, + opath + ); + } + else + { + g_string_free(uuid, TRUE); + } + } + } + + g_object_unref(object); +} + +static void on_sequence_complete(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result) +{ + CONNECT_CONTEXT* context = (CONNECT_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + context->handle_data->state = BLEIO_GATT_STATE_CONNECTED; + + // fetch list of objects on the object manager + GList* objects_list = g_dbus_object_manager_get_objects( + (GDBusObjectManager*)context->handle_data->object_manager + ); + if (objects_list == NULL) + { + on_sequence_error(async_seq_handle, NULL); + } + else + { + // iterate through each object and add each characteristic + // that is a child of the device object path to the map + g_list_foreach(objects_list, process_object, context); + g_list_free(objects_list); + + /*Codes_SRS_BLEIO_GATT_13_011: [ When the connect operation to the device has been completed, the callback function pointed at by on_bleio_gatt_connect_complete shall be invoked. ]*/ + /*Codes_SRS_BLEIO_GATT_13_012: [ When on_bleio_gatt_connect_complete is invoked the value passed in callback_context to BLEIO_gatt_connect shall be passed along to on_bleio_gatt_connect_complete. ]*/ + /*Codes_SRS_BLEIO_GATT_13_013: [ The connect_result parameter of the on_bleio_gatt_connect_complete callback shall indicate the status of the connect operation. ]*/ + // invoke the user's callback + context->on_connect_complete( + (BLEIO_GATT_HANDLE)context->handle_data, + context->callback_context, + BLEIO_GATT_CONNECT_OK + ); + + free_context(context); + } +} + +static void on_sequence_error(GIO_ASYNCSEQ_HANDLE async_seq_handle, const GError* error) +{ + LogError("Connect failed with - %s", + (error != NULL && error->message != NULL) ? error->message : "[unknown error]"); + + CONNECT_CONTEXT* context = (CONNECT_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + context->handle_data->state = BLEIO_GATT_STATE_ERROR; + + /*Codes_SRS_BLEIO_GATT_13_011: [ When the connect operation to the device has been completed, the callback function pointed at by on_bleio_gatt_connect_complete shall be invoked. ]*/ + /*Codes_SRS_BLEIO_GATT_13_012: [ When on_bleio_gatt_connect_complete is invoked the value passed in callback_context to BLEIO_gatt_connect shall be passed along to on_bleio_gatt_connect_complete. ]*/ + /*Codes_SRS_BLEIO_GATT_13_013: [ The connect_result parameter of the on_bleio_gatt_connect_complete callback shall indicate the status of the connect operation. ]*/ + // invoke the user's callback with an error status + context->on_connect_complete( + (BLEIO_GATT_HANDLE)context->handle_data, + context->callback_context, + BLEIO_GATT_CONNECT_ERROR + ); + free_context(context); +} + +static void free_context(CONNECT_CONTEXT* context) +{ + if (context->device_path != NULL) + { + g_string_free(context->device_path, TRUE); + } + + GIO_Async_Seq_Destroy(context->async_seq); + free(context); +} diff --git a/modules/ble/src/ble_gatt_io_linux_disconnect.c b/modules/ble/src/ble_gatt_io_linux_disconnect.c new file mode 100644 index 00000000..dad4e36b --- /dev/null +++ b/modules/ble/src/ble_gatt_io_linux_disconnect.c @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include +#include + +#include "bluez_device.h" +#include "bluez_characteristic.h" + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" + +#include "gio_async_seq.h" +#include "ble_gatt_io.h" +#include "ble_gatt_io_linux_common.h" + +typedef struct DISCONNECT_CONTEXT_TAG +{ + BLEIO_GATT_HANDLE_DATA* handle_data; + ON_BLEIO_GATT_DISCONNECT_COMPLETE on_disconnect_complete; + void* callback_context; +}DISCONNECT_CONTEXT; + +// called when the device completes disconnection +static void on_device_disconnect(GObject* source_object, GAsyncResult* result, gpointer user_data); + +void BLEIO_gatt_disconnect( + BLEIO_GATT_HANDLE bleio_gatt_handle, + ON_BLEIO_GATT_DISCONNECT_COMPLETE on_bleio_gatt_disconnect_complete, + void* callback_context +) +{ + BLEIO_GATT_HANDLE_DATA* handle_data = (BLEIO_GATT_HANDLE_DATA*)bleio_gatt_handle; + if (bleio_gatt_handle != NULL) + { + if (handle_data->state == BLEIO_GATT_STATE_CONNECTED) + { + /*Codes_SRS_BLEIO_GATT_13_054: [ BLEIO_gatt_disconnect shall do nothing if an underlying platform call fails. ]*/ + DISCONNECT_CONTEXT* context = (DISCONNECT_CONTEXT*)malloc(sizeof(DISCONNECT_CONTEXT)); + if (context != NULL) + { + context->handle_data = handle_data; + context->on_disconnect_complete = on_bleio_gatt_disconnect_complete; + context->callback_context = callback_context; + + /*Codes_SRS_BLEIO_GATT_13_014: [ BLEIO_gatt_disconnect shall asynchronously disconnect from the BLE device if an open connection exists. ]*/ + bluez_device__call_disconnect( + handle_data->device, + NULL, + on_device_disconnect, + context + ); + } + else + { + LogError("malloc failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_015: [ BLEIO_gatt_disconnect shall do nothing if an open connection does not exist when it is called. ]*/ + LogError("Object is already disconnected from the device."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_016: [ BLEIO_gatt_disconnect shall do nothing if bleio_gatt_handle is NULL. ]*/ + LogError("Invalid args."); + } +} + +static void on_device_disconnect( + GObject* source_object, + GAsyncResult* result, + gpointer user_data +) +{ + DISCONNECT_CONTEXT* context = (DISCONNECT_CONTEXT*)user_data; + bluez_device__call_disconnect_finish(context->handle_data->device, result, NULL); + + /*Codes_SRS_BLEIO_GATT_13_050: [ When the disconnect operation has been completed, the callback function pointed at by on_bleio_gatt_disconnect_complete shall be invoked if it is not NULL. ]*/ + /*Codes_SRS_BLEIO_GATT_13_049: [ When on_bleio_gatt_disconnect_complete is invoked the value passed in callback_context to BLEIO_gatt_disconnect shall be passed along to on_bleio_gatt_disconnect_complete. ]*/ + if(context->on_disconnect_complete != NULL) + { + context->on_disconnect_complete( + (BLEIO_GATT_HANDLE)context->handle_data, + context->callback_context + ); + } + + free(context); +} diff --git a/modules/ble/src/ble_gatt_io_linux_read.c b/modules/ble/src/ble_gatt_io_linux_read.c new file mode 100644 index 00000000..a8199657 --- /dev/null +++ b/modules/ble/src/ble_gatt_io_linux_read.c @@ -0,0 +1,313 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include +#include + +#include "bluez_device.h" +#include "bluez_characteristic.h" + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" + +#include "gio_async_seq.h" +#include "ble_gatt_io.h" +#include "ble_gatt_io_linux_common.h" + +typedef struct READ_CONTEXT_TAG +{ + BLEIO_GATT_HANDLE_DATA* handle_data; + GString* object_path; + bluezcharacteristic* characteristic; + GIO_ASYNCSEQ_HANDLE async_seq; + ON_BLEIO_GATT_ATTRIB_READ_COMPLETE on_read_complete; + void* callback_context; +}READ_CONTEXT; + +// called when an error occurs in an async call +static void on_sequence_error(GIO_ASYNCSEQ_HANDLE async_seq_handle, const GError* error); + +// called when the entire async sequence completes +static void on_sequence_complete(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result); + +// async sequence functions +static void create_characteristic(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result, gpointer callback_context, GAsyncReadyCallback async_callback); +static gpointer create_characteristic_finish(GIO_ASYNCSEQ_HANDLE async_seq_handle, GAsyncResult* result, GError** error); + +static void read_characteristic(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result, gpointer callback_context, GAsyncReadyCallback async_callback); +static gpointer read_characteristic_finish(GIO_ASYNCSEQ_HANDLE async_seq_handle, GAsyncResult* result, GError** error); + +// utilities +static void free_context(READ_CONTEXT* context); + +int BLEIO_gatt_read_char_by_uuid( + BLEIO_GATT_HANDLE bleio_gatt_handle, + const char* ble_uuid, + ON_BLEIO_GATT_ATTRIB_READ_COMPLETE on_bleio_gatt_attrib_read_complete, + void* callback_context +) +{ + int result; + BLEIO_GATT_HANDLE_DATA* handle_data = (BLEIO_GATT_HANDLE_DATA*)bleio_gatt_handle; + + /*Codes_SRS_BLEIO_GATT_13_027: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if bleio_gatt_handle is NULL. ]*/ + /*Codes_SRS_BLEIO_GATT_13_028: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if on_bleio_gatt_attrib_read_complete is NULL. ]*/ + /*Codes_SRS_BLEIO_GATT_13_051: [BLEIO_gatt_read_char_by_uuid shall return a non - zero value if ble_uuid is NULL. ] */ + /*Codes_SRS_BLEIO_GATT_13_052: [BLEIO_gatt_read_char_by_uuid shall return a non - zero value if the object is not in a connected state. ] */ + if ( + bleio_gatt_handle != NULL && + ble_uuid != NULL && + on_bleio_gatt_attrib_read_complete != NULL && + handle_data->state == BLEIO_GATT_STATE_CONNECTED + ) + { + GString* uuid = g_string_new(ble_uuid); + if(uuid != NULL) + { + GString* object_path = g_tree_lookup(handle_data->char_object_path_map, uuid); + if(object_path != NULL) + { + // now that we have the object path we don't need the UUID anymore + g_string_free(uuid, TRUE); + + READ_CONTEXT *context = (READ_CONTEXT *)malloc(sizeof(READ_CONTEXT)); + if (context != NULL) + { + // create async sequence + context->async_seq = GIO_Async_Seq_Create( + context, + on_sequence_error, + on_sequence_complete + ); + if (context->async_seq != NULL) + { + // setup call sequence + GIO_ASYNCSEQ_RESULT seq_result = GIO_Async_Seq_Add( + context->async_seq, NULL, + + // create an instance of the characteristic + create_characteristic, create_characteristic_finish, + + // read the characteristic + read_characteristic, read_characteristic_finish, + + // sentinel value to signal end of sequence + NULL + ); + if (seq_result == GIO_ASYNCSEQ_OK) + { + context->handle_data = handle_data; + context->object_path = object_path; + context->characteristic = NULL; + context->on_read_complete = on_bleio_gatt_attrib_read_complete; + context->callback_context = callback_context; + + /*Codes_SRS_BLEIO_GATT_13_031: [ BLEIO_gatt_read_char_by_uuid shall asynchronously initiate a read characteristic operation using the specified UUID. ]*/ + // kick-off the sequence + if(GIO_Async_Seq_Run_Async(context->async_seq) == GIO_ASYNCSEQ_OK) + { + /*Codes_SRS_BLEIO_GATT_13_030: [ BLEIO_gatt_read_char_by_uuid shall return 0 (zero) if the read characteristic operation is successful. ]*/ + result = 0; + } + else + { + result = __LINE__; + GIO_Async_Seq_Destroy(context->async_seq); + free(context); + LogError("GIO_Async_Seq_Run failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + result = __LINE__; + GIO_Async_Seq_Destroy(context->async_seq); + free(context); + LogError("GIO_Async_Seq_Add failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + result = __LINE__; + free(context); + LogError("GIO_Async_Seq_Create failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + result = __LINE__; + LogError("malloc failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + result = __LINE__; + g_string_free(uuid, TRUE); + LogError("g_tree_lookup() failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + result = __LINE__; + LogError("g_string_new() failed."); + } + } + else + { + result = __LINE__; + LogError("Invalid args or the state of the object is unexpected."); + } + + return result; +} + +static void create_characteristic( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +) +{ + READ_CONTEXT* context = (READ_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + bluez_characteristic__proxy_new( + context->handle_data->bus, + G_DBUS_PROXY_FLAGS_NONE, + "org.bluez", + context->object_path->str, + NULL, + async_callback, + async_seq_handle + ); +} + +static gpointer create_characteristic_finish( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + GAsyncResult* result, + GError** error +) +{ + return bluez_characteristic__proxy_new_finish(result, error); +} + +static void read_characteristic( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +) +{ + READ_CONTEXT* context = (READ_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + context->characteristic = (bluezcharacteristic*)previous_result; + + bluez_characteristic__call_read_value( + context->characteristic, + NULL, + async_callback, + async_seq_handle + ); +} + +static gpointer read_characteristic_finish( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + GAsyncResult* result, + GError** error +) +{ + READ_CONTEXT* context = (READ_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + GBytes* result_value; + GVariant* ret = g_dbus_proxy_call_finish( + G_DBUS_PROXY(context->characteristic), + result, + error + ); + if (ret != NULL && *error == NULL) + { + result_value = g_variant_get_data_as_bytes(ret); + g_variant_unref(ret); + } + else + { + result_value = NULL; + } + + return result_value; +} + +static void on_sequence_complete(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result) +{ + READ_CONTEXT* context = (READ_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + GBytes* data = (GBytes*)previous_result; + const unsigned char* buffer; + size_t size; + + if (data != NULL) + { + buffer = g_bytes_get_data(data, &size); + } + else + { + buffer = NULL; + size = 0; + } + + /*Codes_SRS_BLEIO_GATT_13_032: [ BLEIO_gatt_read_char_by_uuid shall invoke on_bleio_gatt_attrib_read_complete when the read operation completes. ]*/ + /*Codes_SRS_BLEIO_GATT_13_033: [ BLEIO_gatt_read_char_by_uuid shall pass the value of the callback_context parameter to on_bleio_gatt_attrib_read_complete as the context parameter when it is invoked. ]*/ + /*Codes_SRS_BLEIO_GATT_13_034: [BLEIO_gatt_read_char_by_uuid, when successful, shall supply the data that has been read to the on_bleio_gatt_attrib_read_complete callback along with the value BLEIO_GATT_OK for the result parameter.]*/ + context->on_read_complete( + (BLEIO_GATT_HANDLE)context->handle_data, + context->callback_context, + BLEIO_GATT_OK, + buffer, + size + ); + + g_bytes_unref(data); + free_context(context); +} + +static void on_sequence_error(GIO_ASYNCSEQ_HANDLE async_seq_handle, const GError* error) +{ + READ_CONTEXT* context = (READ_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + + if (error != NULL) + { + LogError("Read characteristic failed with - %s", error->message); + } + + /*Codes_SRS_BLEIO_GATT_13_032: [ BLEIO_gatt_read_char_by_uuid shall invoke on_bleio_gatt_attrib_read_complete when the read operation completes. ]*/ + /*Codes_SRS_BLEIO_GATT_13_033: [ BLEIO_gatt_read_char_by_uuid shall pass the value of the callback_context parameter to on_bleio_gatt_attrib_read_complete as the context parameter when it is invoked. ]*/ + /*Codes_SRS_BLEIO_GATT_13_035: [ When an error occurs asynchronously, the value BLEIO_GATT_ERROR shall be passed for the result parameter of the on_bleio_gatt_attrib_read_complete callback. ]*/ + context->on_read_complete( + (BLEIO_GATT_HANDLE)context->handle_data, + context->callback_context, + BLEIO_GATT_ERROR, + NULL, 0 + ); + + free_context(context); +} + +static void free_context(READ_CONTEXT* context) +{ + // we don't free context->object_path because that string lives in + // the uuid->object_path map in the BLEIO_GATT_HANDLE and will be + // reused for future reads + + if (context->characteristic != NULL) + { + g_object_unref(context->characteristic); + } + + GIO_Async_Seq_Destroy(context->async_seq); + free(context); +} diff --git a/modules/ble/src/ble_gatt_io_linux_write.c b/modules/ble/src/ble_gatt_io_linux_write.c new file mode 100644 index 00000000..75d5d2b6 --- /dev/null +++ b/modules/ble/src/ble_gatt_io_linux_write.c @@ -0,0 +1,330 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include +#include + +#include "bluez_device.h" +#include "bluez_characteristic.h" + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" + +#include "gio_async_seq.h" +#include "ble_gatt_io.h" +#include "ble_gatt_io_linux_common.h" + +typedef struct WRITE_CONTEXT_TAG +{ + BLEIO_GATT_HANDLE_DATA* handle_data; + GString* object_path; + bluezcharacteristic* characteristic; + GBytes* data; + GIO_ASYNCSEQ_HANDLE async_seq; + ON_BLEIO_GATT_ATTRIB_WRITE_COMPLETE on_write_complete; + void* callback_context; +}WRITE_CONTEXT; + +// called when an error occurs in an async call +static void on_sequence_error(GIO_ASYNCSEQ_HANDLE async_seq_handle, const GError* error); + +// called when the entire async sequence completes +static void on_sequence_complete(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result); + +// async sequence functions +static void create_characteristic(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result, gpointer callback_context, GAsyncReadyCallback async_callback); +static gpointer create_characteristic_finish(GIO_ASYNCSEQ_HANDLE async_seq_handle, GAsyncResult* result, GError** error); + +static void write_characteristic(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result, gpointer callback_context, GAsyncReadyCallback async_callback); +static gpointer write_characteristic_finish(GIO_ASYNCSEQ_HANDLE async_seq_handle, GAsyncResult* result, GError** error); + +// utilities +static void free_context(WRITE_CONTEXT* context); + +int BLEIO_gatt_write_char_by_uuid( + BLEIO_GATT_HANDLE bleio_gatt_handle, + const char* ble_uuid, + const unsigned char* buffer, + size_t size, + ON_BLEIO_GATT_ATTRIB_WRITE_COMPLETE on_bleio_gatt_attrib_write_complete, + void* callback_context +) +{ + int result; + BLEIO_GATT_HANDLE_DATA* handle_data = (BLEIO_GATT_HANDLE_DATA*)bleio_gatt_handle; + + /*Codes_SRS_BLEIO_GATT_13_036: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if bleio_gatt_handle is NULL. ]*/ + /*Codes_SRS_BLEIO_GATT_13_045: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if buffer is NULL. ]*/ + /*Codes_SRS_BLEIO_GATT_13_048: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if ble_uuid is NULL. ]*/ + /*Codes_SRS_BLEIO_GATT_13_046: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if size is equal to 0 (zero). ]*/ + /*Codes_SRS_BLEIO_GATT_13_037: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if on_bleio_gatt_attrib_write_complete is NULL. ]*/ + /*Codes_SRS_BLEIO_GATT_13_053: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an active connection to the device does not exist. ]*/ + if ( + bleio_gatt_handle != NULL && + ble_uuid != NULL && + on_bleio_gatt_attrib_write_complete != NULL && + buffer != NULL && + size > 0 && + handle_data->state == BLEIO_GATT_STATE_CONNECTED + ) + { + GString* uuid = g_string_new(ble_uuid); + if(uuid != NULL) + { + GString* object_path = g_tree_lookup(handle_data->char_object_path_map, uuid); + if(object_path != NULL) + { + // now that we have the object path we don't need the UUID anymore + g_string_free(uuid, TRUE); + + WRITE_CONTEXT *context = (WRITE_CONTEXT *)malloc(sizeof(WRITE_CONTEXT)); + if (context != NULL) + { + context->data = g_bytes_new(buffer, size); + if(context->data != NULL) + { + // create async sequence + context->async_seq = GIO_Async_Seq_Create( + context, + on_sequence_error, + on_sequence_complete + ); + if (context->async_seq != NULL) + { + // setup call sequence + GIO_ASYNCSEQ_RESULT seq_result = GIO_Async_Seq_Add( + context->async_seq, NULL, + + // create an instance of the characteristic + create_characteristic, create_characteristic_finish, + + // write the characteristic + write_characteristic, write_characteristic_finish, + + // sentinel value to signal end of sequence + NULL + ); + if (seq_result == GIO_ASYNCSEQ_OK) + { + context->handle_data = handle_data; + context->object_path = object_path; + context->characteristic = NULL; + context->on_write_complete = on_bleio_gatt_attrib_write_complete; + context->callback_context = callback_context; + + /*Codes_SRS_BLEIO_GATT_13_040: [ BLEIO_gatt_write_char_by_uuid shall asynchronously initiate a write characteristic operation using the specified UUID. ]*/ + // kick-off the sequence + if(GIO_Async_Seq_Run_Async(context->async_seq) == GIO_ASYNCSEQ_OK) + { + /*Codes_SRS_BLEIO_GATT_13_039: [ BLEIO_gatt_write_char_by_uuid shall return 0 (zero) if the write characteristic operation is successful. ]*/ + result = 0; + } + else + { + result = __LINE__; + GIO_Async_Seq_Destroy(context->async_seq); + g_bytes_unref(context->data); + free(context); + LogError("GIO_Async_Seq_Run_Async failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + result = __LINE__; + GIO_Async_Seq_Destroy(context->async_seq); + g_bytes_unref(context->data); + free(context); + LogError("GIO_Async_Seq_Add failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + result = __LINE__; + g_bytes_unref(context->data); + free(context); + LogError("GIO_Async_Seq_Create failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + result = __LINE__; + free(context); + LogError("g_byte_array_sized_new failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + result = __LINE__; + LogError("malloc failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + result = __LINE__; + g_string_free(uuid, TRUE); + LogError("g_tree_lookup() failed."); + } + } + else + { + /*Codes_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + result = __LINE__; + LogError("g_string_new() failed."); + } + } + else + { + result = __LINE__; + LogError("Invalid args or the state of the object is unexpected."); + } + + return result; +} + +static void create_characteristic( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +) +{ + WRITE_CONTEXT* context = (WRITE_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + bluez_characteristic__proxy_new( + context->handle_data->bus, + G_DBUS_PROXY_FLAGS_NONE, + "org.bluez", + context->object_path->str, + NULL, + async_callback, + async_seq_handle + ); +} + +static gpointer create_characteristic_finish( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + GAsyncResult* result, + GError** error +) +{ + return bluez_characteristic__proxy_new_finish(result, error); +} + +static void write_characteristic( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer previous_result, + gpointer callback_context, + GAsyncReadyCallback async_callback +) +{ + WRITE_CONTEXT* context = (WRITE_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + context->characteristic = (bluezcharacteristic*)previous_result; + + // we don't check if this fails; d-bus will raise an error if no params + // are passed (which is what NULL would mean) for the "WriteValue" + // method when it expects one + gsize size_data; + gconstpointer data = g_bytes_get_data(context->data, &size_data); + GVariant* var = g_variant_new_fixed_array( + G_VARIANT_TYPE_BYTE, + data, + size_data, + sizeof(unsigned char) + ); + + g_dbus_proxy_call( + G_DBUS_PROXY(context->characteristic), + "WriteValue", + g_variant_new_tuple(&var, 1), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + async_callback, + async_seq_handle + ); +} + +static gpointer write_characteristic_finish( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + GAsyncResult* result, + GError** error +) +{ + WRITE_CONTEXT* context = (WRITE_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + + gboolean ret = bluez_characteristic__call_write_value_finish( + context->characteristic, + result, + error + ); + + return (gpointer)(uintptr_t)ret; +} + +static void on_sequence_complete(GIO_ASYNCSEQ_HANDLE async_seq_handle, gpointer previous_result) +{ + WRITE_CONTEXT* context = (WRITE_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + + /*Codes_SRS_BLEIO_GATT_13_041: [ BLEIO_gatt_write_char_by_uuid shall invoke on_bleio_gatt_attrib_write_complete when the write operation completes. ]*/ + /*Codes_SRS_BLEIO_GATT_13_042: [ BLEIO_gatt_write_char_by_uuid shall pass the value of the callback_context parameter to on_bleio_gatt_attrib_write_complete as the context parameter when it is invoked. ]*/ + /*Codes_SRS_BLEIO_GATT_13_043: [ BLEIO_gatt_write_char_by_uuid, when successful, shall supply the value BLEIO_GATT_OK for the result parameter. ]*/ + context->on_write_complete( + (BLEIO_GATT_HANDLE)context->handle_data, + context->callback_context, + BLEIO_GATT_OK + ); + + free_context(context); +} + +static void on_sequence_error(GIO_ASYNCSEQ_HANDLE async_seq_handle, const GError* error) +{ + WRITE_CONTEXT* context = (WRITE_CONTEXT*)GIO_Async_Seq_GetContext(async_seq_handle); + + if (error != NULL) + { + LogError("Write characteristic failed with - %s", error->message); + } + + /*Codes_SRS_BLEIO_GATT_13_041: [ BLEIO_gatt_write_char_by_uuid shall invoke on_bleio_gatt_attrib_write_complete when the write operation completes. ]*/ + /*Codes_SRS_BLEIO_GATT_13_042: [ BLEIO_gatt_write_char_by_uuid shall pass the value of the callback_context parameter to on_bleio_gatt_attrib_write_complete as the context parameter when it is invoked. ]*/ + /*Codes_SRS_BLEIO_GATT_13_044: [ When an error occurs asynchronously, the value BLEIO_GATT_ERROR shall be passed for the result parameter of the on_bleio_gatt_attrib_write_complete callback. ]*/ + context->on_write_complete( + (BLEIO_GATT_HANDLE)context->handle_data, + context->callback_context, + BLEIO_GATT_ERROR + ); + + free_context(context); +} + +static void free_context(WRITE_CONTEXT* context) +{ + // we don't free context->object_path because that string lives in + // the uuid->object_path map in the BLEIO_GATT_HANDLE and will be + // reused for future reads + + if (context->characteristic != NULL) + { + g_object_unref(context->characteristic); + } + + if (context->data != NULL) + { + g_bytes_unref(context->data); + } + + GIO_Async_Seq_Destroy(context->async_seq); + free(context); +} + diff --git a/modules/ble/src/ble_gatt_io_windows.c b/modules/ble/src/ble_gatt_io_windows.c new file mode 100644 index 00000000..783b530d --- /dev/null +++ b/modules/ble/src/ble_gatt_io_windows.c @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/gballoc.h" + +#include "ble_gatt_io.h" + +BLEIO_GATT_HANDLE BLEIO_gatt_create( + const BLE_DEVICE_CONFIG* config +) +{ + LogError("BLEIO_gatt_create not implemented on Windows yet."); + return NULL; +} + +void BLEIO_gatt_destroy( + BLEIO_GATT_HANDLE bleio_gatt_handle +) +{ + LogError("BLEIO_gatt_destroy not implemented on Windows yet."); +} + +int BLEIO_gatt_connect( + BLEIO_GATT_HANDLE bleio_gatt_handle, + ON_BLEIO_GATT_CONNECT_COMPLETE on_bleio_gatt_connect_complete, + void* callback_context +) +{ + LogError("BLEIO_gatt_connect not implemented on Windows yet."); + return __LINE__; +} + +void BLEIO_gatt_disconnect( + BLEIO_GATT_HANDLE bleio_gatt_handle, + ON_BLEIO_GATT_DISCONNECT_COMPLETE on_bleio_gatt_disconnect_complete, + void* callback_context +) +{ + LogError("BLEIO_gatt_disconnect not implemented on Windows yet."); +} + +int BLEIO_gatt_read_char_by_uuid( + BLEIO_GATT_HANDLE bleio_gatt_handle, + const char* ble_uuid, + ON_BLEIO_GATT_ATTRIB_READ_COMPLETE on_bleio_gatt_attrib_read_complete, + void* callback_context +) +{ + LogError("BLEIO_gatt_read_char_by_uuid not implemented on Windows yet."); + return __LINE__; +} + +int BLEIO_gatt_write_char_by_uuid( + BLEIO_GATT_HANDLE bleio_gatt_handle, + const char* ble_uuid, + const unsigned char* buffer, + size_t size, + ON_BLEIO_GATT_ATTRIB_WRITE_COMPLETE on_bleio_gatt_attrib_write_complete, + void* callback_context +) +{ + LogError("BLEIO_gatt_write_char_by_uuid not implemented on Windows yet."); + return __LINE__; +} diff --git a/modules/ble/src/ble_hl.c b/modules/ble/src/ble_hl.c new file mode 100644 index 00000000..514cb92c --- /dev/null +++ b/modules/ble/src/ble_hl.c @@ -0,0 +1,644 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include + +#include "azure_c_shared_utility/gballoc.h" + +/*because it is linked statically, this include will bring in some uniquely (by convention) named functions*/ +#include "ble.h" +#include "ble_hl.h" +#include "ble_utils.h" + +#include "azure_c_shared_utility/iot_logging.h" +#include "parson.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/base64.h" +#include "messageproperties.h" +#include "message.h" +#include "azure_c_shared_utility/constmap.h" +#include "azure_c_shared_utility/map.h" + +typedef struct BLE_HL_HANDLE_DATA_TAG +{ + MESSAGE_BUS_HANDLE bus; + STRING_HANDLE mac_address; + MODULE_HANDLE module_handle; +}BLE_HL_HANDLE_DATA; + +static bool parse_instruction(const char* type, JSON_Object* instr, BLE_INSTRUCTION* ble_instr, size_t index); +static VECTOR_HANDLE parse_instructions(JSON_Array* instructions); +static bool parse_read_periodic(JSON_Object* instr, BLE_INSTRUCTION* ble_instr); +static bool parse_write(JSON_Object* instr, BLEIO_SEQ_INSTRUCTION_TYPE type, BLE_INSTRUCTION* ble_instr, size_t index); +static void free_instruction(BLE_INSTRUCTION* instr); +static void free_instructions(VECTOR_HANDLE instructions); + +static MODULE_HANDLE BLE_HL_Create(MESSAGE_BUS_HANDLE bus, const void* configuration) +{ + BLE_HL_HANDLE_DATA* result; + + /*Codes_SRS_BLE_HL_13_001: [ BLE_HL_Create shall return NULL if the bus or configuration parameters are NULL. ]*/ + if( + (bus == NULL) || + (configuration == NULL) + ) + { + LogError("NULL parameter detected bus=%p configuration=%p", bus, configuration); + result = NULL; + } + else + { + JSON_Value* json = json_parse_string((const char*)configuration); + if (json == NULL) + { + /*Codes_SRS_BLE_HL_13_002: [ BLE_HL_Create shall return NULL if any of the underlying platform calls fail. ]*/ + LogError("unable to json_parse_string"); + result = NULL; + } + else + { + JSON_Object* root = json_value_get_object(json); + if (root == NULL) + { + /*Codes_SRS_BLE_HL_13_003: [ BLE_HL_Create shall return NULL if the JSON does not start with an object. ]*/ + LogError("unable to json_value_get_object"); + result = NULL; + } + else + { + // get controller index + int controller_index = (int)json_object_get_number(root, "controller_index"); + if (controller_index < 0) + { + /*Codes_SRS_BLE_HL_13_005: [ BLE_HL_Create shall return NULL if the controller_index value in the JSON is less than zero. ]*/ + LogError("Invalid BLE controller index specified"); + result = NULL; + } + else + { + const char* mac_address = json_object_get_string(root, "device_mac_address"); + if (mac_address == NULL) + { + /*Codes_SRS_BLE_HL_13_004: [ BLE_HL_Create shall return NULL if there is no device_mac_address property in the JSON. ]*/ + LogError("json_object_get_string failed for property 'device_mac_address'"); + result = NULL; + } + else + { + JSON_Array* instructions = json_object_get_array(root, "instructions"); + if (instructions == NULL) + { + /*Codes_SRS_BLE_HL_13_006: [ BLE_HL_Create shall return NULL if the instructions array does not exist in the JSON. ]*/ + LogError("json_object_get_array failed for property 'instructions'"); + result = NULL; + } + else + { + VECTOR_HANDLE ble_instructions = parse_instructions(instructions); + if (ble_instructions == NULL) + { + LogError("parse_instructions returned NULL"); + result = NULL; + } + else + { + BLE_CONFIG ble_config; + if (parse_mac_address( + mac_address, + &(ble_config.device_config.device_addr) + ) == false) + { + /*Codes_SRS_BLE_HL_13_013: [ BLE_HL_Create shall return NULL if the device_mac_address property's value is not a well-formed MAC address. ]*/ + LogError("parse_mac_address returned false"); + free_instructions(ble_instructions); + VECTOR_destroy(ble_instructions); + result = NULL; + } + else + { + result = (BLE_HL_HANDLE_DATA*)malloc(sizeof(BLE_HL_HANDLE_DATA)); + if (result == NULL) + { + /*Codes_SRS_BLE_HL_13_002: [ BLE_HL_Create shall return NULL if any of the underlying platform calls fail. ]*/ + LogError("malloc failed"); + free_instructions(ble_instructions); + VECTOR_destroy(ble_instructions); + } + else + { + result->mac_address = STRING_construct(mac_address); + if (result->mac_address == NULL) + { + LogError("STRING_create failed"); + free_instructions(ble_instructions); + VECTOR_destroy(ble_instructions); + free(result); + result = NULL; + } + else + { + ble_config.device_config.ble_controller_index = controller_index; + ble_config.instructions = ble_instructions; + + /*Codes_SRS_BLE_HL_13_014: [ BLE_HL_Create shall call the underlying module's 'create' function. ]*/ + result->module_handle = MODULE_STATIC_GETAPIS(BLE_MODULE)()->Module_Create( + bus, (const void*)&ble_config + ); + if (result->module_handle == NULL) + { + /*Codes_SRS_BLE_HL_13_022: [ BLE_HL_Create shall return NULL if calling the underlying module's create function fails. ]*/ + LogError("Unable to create BLE low level module"); + free_instructions(ble_instructions); + VECTOR_destroy(ble_instructions); + STRING_delete(result->mac_address); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_BLE_HL_13_023: [ BLE_HL_Create shall return a non-NULL handle if calling the underlying module's create function succeeds. ]*/ + result->bus = bus; + } + } + } + } + } + } + } + } + } + json_value_free(json); + } + } + + return (MODULE_HANDLE)result; +} + +static void free_instruction(BLE_INSTRUCTION* instr) +{ + // free string handle for the characteristic uuid + if(instr->characteristic_uuid != NULL) + { + STRING_delete(instr->characteristic_uuid); + } + + // free write buffer + if( + ( + instr->instruction_type == WRITE_AT_INIT || + instr->instruction_type == WRITE_ONCE || + instr->instruction_type == WRITE_AT_EXIT + ) + && + ( + instr->data.buffer != NULL + ) + ) + { + BUFFER_delete(instr->data.buffer); + } +} + +static void free_instructions(VECTOR_HANDLE instructions) +{ + size_t len = VECTOR_size(instructions); + for(size_t i = 0; i < len; ++i) + { + BLE_INSTRUCTION* instr = (BLE_INSTRUCTION*)VECTOR_element(instructions, i); + free_instruction(instr); + } +} + +static bool parse_read_periodic( + JSON_Object* instr, + BLE_INSTRUCTION* ble_instr +) +{ + ble_instr->instruction_type = READ_PERIODIC; + ble_instr->data.interval_in_ms = (uint32_t)json_object_get_number(instr, "interval_in_ms"); + return ble_instr->data.interval_in_ms > 0; +} + +static bool parse_write( + JSON_Object* instr, + BLEIO_SEQ_INSTRUCTION_TYPE type, + BLE_INSTRUCTION* ble_instr, + size_t index +) +{ + bool result; + ble_instr->instruction_type = type; + const char* base64_encoded_data = json_object_get_string(instr, "data"); + if(base64_encoded_data == NULL) + { + /*Codes_SRS_BLE_HL_13_011: [ BLE_HL_Create shall return NULL if an instruction of type write_at_init or write_at_exit does not have a data property. ]*/ + LogError("json_value_get_string returned NULL for the property 'data' while processing instruction %zu", index); + result = false; + } + else + { + ble_instr->data.buffer = Base64_Decoder(base64_encoded_data); + if(ble_instr->data.buffer == NULL) + { + /*Codes_SRS_BLE_HL_13_012: [ BLE_HL_Create shall return NULL if an instruction of type write_at_init or write_at_exit has a data property whose value does not decode successfully from base 64. ]*/ + LogError("Base64_Decoder returned NULL for the property 'data' while processing instruction %zu", index); + result = false; + } + else + { + result = true; + } + } + + return result; +} + +static bool parse_instruction( + const char* type, + JSON_Object* instr, + BLE_INSTRUCTION* ble_instr, + size_t index +) +{ + bool result; + if(strcmp(type, "read_once") == 0) + { + ble_instr->instruction_type = READ_ONCE; + result = true; + } + else if(strcmp(type, "read_periodic") == 0) + { + if(parse_read_periodic(instr, ble_instr) == false) + { + /*Codes_SRS_BLE_HL_13_010: [ BLE_HL_Create shall return NULL if the interval_in_ms value for a read_periodic instruction isn't greater than zero. ]*/ + LogError("parse_read_periodic returned false while processing instruction %zu", index); + result = false; + } + else + { + result = true; + } + } + else if(strcmp(type, "write_at_init") == 0) + { + if(parse_write(instr, WRITE_AT_INIT, ble_instr, index) == false) + { + LogError("parse_write returned false while processing instruction %zu", index); + result = false; + } + else + { + result = true; + } + } + else if (strcmp(type, "write_once") == 0) + { + if (parse_write(instr, WRITE_ONCE, ble_instr, index) == false) + { + LogError("parse_write returned false while processing instruction %zu", index); + result = false; + } + else + { + result = true; + } + } + else if(strcmp(type, "write_at_exit") == 0) + { + if(parse_write(instr, WRITE_AT_EXIT, ble_instr, index) == false) + { + LogError("parse_write returned false while processing instruction %zu", index); + result = false; + } + else + { + result = true; + } + } + else + { + /*Codes_SRS_BLE_HL_13_021: [ BLE_HL_Create shall return NULL if a given instruction's type property is unrecognized. ]*/ + LogError("Unknown instruction type '%s' encountered for instruction number %zu", type, index); + result = false; + } + + return result; +} + +static VECTOR_HANDLE parse_instructions(JSON_Array* instructions) +{ + VECTOR_HANDLE result; + size_t count = json_array_get_count(instructions); + if (count == 0) + { + /*Codes_SRS_BLE_HL_13_020: [ BLE_HL_Create shall return NULL if the instructions array length is equal to zero. ]*/ + LogError("json_array_get_count returned zero"); + result = NULL; + } + else + { + result = VECTOR_create(sizeof(BLE_INSTRUCTION)); + if (result != NULL) + { + for (size_t i = 0; i < count; ++i) + { + JSON_Object* instr = json_array_get_object(instructions, i); + if (instr == NULL) + { + /*Codes_SRS_BLE_HL_13_007: [ BLE_HL_Create shall return NULL if each instruction is not an object. ]*/ + LogError("json_array_get_object returned NULL for instruction number %zu", i); + free_instructions(result); + VECTOR_destroy(result); + result = NULL; + break; + } + else + { + const char* type = json_object_get_string(instr, "type"); + if (type == NULL) + { + /*Codes_SRS_BLE_HL_13_008: [ BLE_HL_Create shall return NULL if a given instruction does not have a type property. ]*/ + LogError("json_object_get_string returned NULL for the property 'type' for instruction number %zu", i); + free_instructions(result); + VECTOR_destroy(result); + result = NULL; + break; + } + else + { + const char* characteristic_uuid = json_object_get_string(instr, "characteristic_uuid"); + if (characteristic_uuid == NULL) + { + /*Codes_SRS_BLE_HL_13_009: [ BLE_HL_Create shall return NULL if a given instruction does not have a characteristic_uuid property. ]*/ + LogError("json_object_get_string returned NULL for the property 'characteristic_uuid' for instruction number %zu", i); + free_instructions(result); + VECTOR_destroy(result); + result = NULL; + break; + } + else + { + BLE_INSTRUCTION ble_instr = { 0 }; + ble_instr.characteristic_uuid = STRING_construct(characteristic_uuid); + if (ble_instr.characteristic_uuid == NULL) + { + /*Codes_SRS_BLE_HL_13_002: [ BLE_HL_Create shall return NULL if any of the underlying platform calls fail. ]*/ + LogError("STRING_construct returned NULL while processing instruction %zu", i); + free_instructions(result); + VECTOR_destroy(result); + result = NULL; + break; + } + else + { + if (parse_instruction(type, instr, &ble_instr, i) == false) + { + LogError("parse_instruction returned false while processing instruction %zu", i); + STRING_delete(ble_instr.characteristic_uuid); + free_instructions(result); + VECTOR_destroy(result); + result = NULL; + break; + } + else + { + // if we get here then we have a valid instruction + if (VECTOR_push_back(result, &ble_instr, 1) != 0) + { + /*Codes_SRS_BLE_HL_13_002: [ BLE_HL_Create shall return NULL if any of the underlying platform calls fail. ]*/ + LogError("VECTOR_push_back returned a non-zero value while processing instruction %zu", i); + free_instruction(&ble_instr); + free_instructions(result); + VECTOR_destroy(result); + result = NULL; + break; + } + } + } + } + } + } + } + } + else + { + LogError("VECTOR_create returned NULL"); + } + } + + return result; +} + +static void BLE_HL_Destroy(MODULE_HANDLE module) +{ + if(module != NULL) + { + /*Codes_SRS_BLE_HL_13_015: [ BLE_HL_Destroy shall destroy all used resources. ]*/ + BLE_HL_HANDLE_DATA* handle_data = (BLE_HL_HANDLE_DATA*)module; + MODULE_STATIC_GETAPIS(BLE_MODULE)()->Module_Destroy(handle_data->module_handle); + STRING_delete(handle_data->mac_address); + free(handle_data); + } + else + { + /*Codes_SRS_BLE_HL_13_017: [ BLE_HL_Destroy shall do nothing if module is NULL. ]*/ + LogError("'module' is NULL"); + } +} + +static bool recognize_bus_message(BLE_HL_HANDLE_DATA* handle_data, CONSTMAP_HANDLE properties) +{ + bool result; + const char * message_mac = ConstMap_GetValue(properties, GW_MAC_ADDRESS_PROPERTY); + if (message_mac != NULL && (strcmp(message_mac, STRING_c_str(handle_data->mac_address)) == 0)) + { + const char * message_source = ConstMap_GetValue(properties, GW_SOURCE_PROPERTY); + if ((message_source != NULL) && (strcmp(message_source, GW_IDMAP_MODULE) == 0)) + { + result = true; /* recognized */ + } + else + { + /*Codes_SRS_BLE_HL_17_004: [ If messageHandle properties does not contain "source" property, then this function shall return. ]*/ + /*Codes_SRS_BLE_HL_17_005: [ If the source of the message properties is not "mapping", then this function shall return. ]*/ + result = false; /* unrecognized */ + } + } + else + { + /*Codes_SRS_BLE_HL_17_003: [ If macAddress of the message property does not match this module's MAC address, then this function shall return. ]*/ + /*Codes_SRS_BLE_HL_17_002: [ If messageHandle properties does not contain "macAddress" property, then this function shall return. ]*/ + result = false; /* unrecognized */ + } + return result; +} + +static int forward_new_message(BLE_HL_HANDLE_DATA* handle_data, CONSTMAP_HANDLE properties, BLE_INSTRUCTION* ble_instr) +{ + int result; + /*Codes_SRS_BLE_HL_17_018: [ BLE_HL_Receive shall call ConstMap_CloneWriteable on the message properties. ]*/ + MAP_HANDLE new_message_props = ConstMap_CloneWriteable(properties); + if (new_message_props != NULL) + { + /*Codes_SRS_BLE_HL_17_020: [ BLE_HL_Receive shall call Map_AddOrUpdate with key of "source" and value of "BLE". ]*/ + if (Map_AddOrUpdate(new_message_props, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_COMMAND) == MAP_OK) + { + MESSAGE_CONFIG cfg; + cfg.size = sizeof(BLE_INSTRUCTION); + cfg.source = (const unsigned char *)ble_instr; + cfg.sourceProperties = new_message_props; + /*Codes_SRS_BLE_HL_17_023: [ BLE_HL_Receive shall create a new message by calling Message_Create with new map and BLE_INSTRUCTION as the buffer. ]*/ + MESSAGE_HANDLE new_message_handle = Message_Create(&cfg); + if (new_message_handle != NULL) + { + /*Codes_SRS_BLE_HL_13_018: [ BLE_HL_Receive shall forward new message to the underlying module. ]*/ + MODULE_STATIC_GETAPIS(BLE_MODULE)()->Module_Receive(handle_data->module_handle, new_message_handle); + Message_Destroy(new_message_handle); + result = 0; + } + else + { + /*Codes_SRS_BLE_HL_17_024: [ If creating new message fails, BLE_HL_Receive shall deallocate all resources and return. ]*/ + LogError("Forward message creation failed"); + result = __LINE__; + } + } + else + { + LogError("Unable to set properties"); + result = __LINE__; + } + Map_Destroy(new_message_props); + } + else + { + /*Codes_SRS_BLE_HL_17_019: [ If ConstMap_CloneWriteable fails, BLE_HL_Receive shall return. ]*/ + LogError("Unable to get writeable properties"); + result = __LINE__; + } + return result; +} + +static void BLE_HL_Receive(MODULE_HANDLE module, MESSAGE_HANDLE message_handle) +{ + /*Codes_SRS_BLE_HL_13_016: [ BLE_HL_Receive shall do nothing if module is NULL. ]*/ + /*Codes_SRS_BLE_HL_17_001: [ BLE_HL_Receive shall do nothing if message_handle is NULL. ]*/ + if(module != NULL && message_handle != NULL) + { + /*Codes_SRS_BLE_HL_13_018: [ BLE_HL_Receive shall forward the call to the underlying module. ]*/ + BLE_HL_HANDLE_DATA* handle_data = (BLE_HL_HANDLE_DATA*)module; + + CONSTMAP_HANDLE properties = Message_GetProperties(message_handle); + if (properties != NULL) + { + if (recognize_bus_message(handle_data, properties) == true) + { + const CONSTBUFFER * message_content = Message_GetContent(message_handle); + if (message_content != NULL) + { + /*Codes_SRS_BLE_HL_17_006: [ BLE_HL_Receive shall parse the message contents as a JSON object. ]*/ + /*Codes_SRS_BLE_HL_17_007: [ If the message contents do not parse, then BLE_HL_Receive shall return. ]*/ + JSON_Value* json = json_parse_string((const char*)(message_content->buffer)); + if (json != NULL) + { + JSON_Object* instr = json_value_get_object(json); + if (instr != NULL) + { + const char* type = json_object_get_string(instr, "type"); + if (type != NULL) + { + /*Codes_SRS_BLE_HL_17_008: [ BLE_HL_Receive shall return if the JSON object does not contain the following fields: "type", "characteristic_uuid", and "data". ]*/ + const char* characteristic_uuid = json_object_get_string(instr, "characteristic_uuid"); + if (characteristic_uuid != NULL) + { + BLE_INSTRUCTION ble_instr = { 0 }; + /*Codes_SRS_BLE_HL_17_012: [ BLE_HL_Receive shall create a STRING_HANDLE from the characteristic_uuid data field. ]*/ + /*Codes_SRS_BLE_HL_17_016: [ BLE_HL_Receive shall set characteristic_uuid to the created STRING. ]*/ + ble_instr.characteristic_uuid = STRING_construct(characteristic_uuid); + if (ble_instr.characteristic_uuid != NULL) + { + /*Codes_SRS_BLE_HL_17_014: [ BLE_HL_Receive shall parse the json object to fill in a new BLE_INSTRUCTION. ]*/ + if (parse_instruction(type, instr, &ble_instr, 0) == true) + { + if (forward_new_message(handle_data, properties, &ble_instr) != 0) + { + free_instruction(&ble_instr); + } + } + else + { + /*Codes_SRS_BLE_HL_17_026: [ If the json object does not parse, BLE_HL_Receive shall return. ]*/ + /*Codes_SRS_BLE_HL_17_008: [ BLE_HL_Receive shall return if the JSON object does not contain the following fields: "type", "characteristic_uuid", and "data". ]*/ + LogError("Not a valid BLE instruction"); + free_instruction(&ble_instr); + } + } + else + { + /*Codes_SRS_BLE_HL_17_013: [ If the string creation fails, BLE_HL_Receive shall return. ]*/ + LogError("Characteristic uuid string creation failed."); + } + } + else + { + LogError("Characteristic uuid not found"); + } + } + else + { + LogError("BLE Instruction type not found"); + } + } + else + { + LogError("JSON Object expected, not received."); + } + json_value_free(json); + } + else + { + LogError("JSON parsing failed"); + } + } + else + { + LogError("No Message Content"); + } + } + /*Codes_SRS_BLE_HL_17_025: [ BLE_HL_Receive shall free all resources created. ]*/ + ConstMap_Destroy(properties); + } + } + else + { + /*Codes_SRS_BLE_HL_13_016: [ BLE_HL_Receive shall do nothing if module is NULL. ]*/ + LogError("'module' is NULL"); + } +} + +/* + * Required for all modules: the public API and the designated implementation functions. + */ +static const MODULE_APIS BLE_HL_APIS_all = +{ + BLE_HL_Create, + BLE_HL_Destroy, + BLE_HL_Receive +}; + +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(BLE_MODULE_HL)(void) +#else +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + /*Codes_SRS_BLE_HL_13_019: [ Module_GetAPIS shall return a non-NULL pointer to a structure of type MODULE_APIS that has all fields initialized to non-NULL values. ]*/ + return &BLE_HL_APIS_all; +} \ No newline at end of file diff --git a/modules/ble/src/ble_utils.c b/modules/ble/src/ble_utils.c new file mode 100644 index 00000000..60306826 --- /dev/null +++ b/modules/ble/src/ble_utils.c @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include + +#include "ble_gatt_io.h" +#include "ble_utils.h" + +bool parse_mac_address(const char* mac_address_str, BLE_MAC_ADDRESS* mac_address) +{ + bool result; + + // mac address has to be in the format XX:XX:XX:XX:XX:XX where X is a value + // in the range 0-F hex base + if (strlen(mac_address_str) != 17) + { + result = false; + } + else + { + // taken from http://stackoverflow.com/a/20553913 + int values[6]; + char unexpected; + if (sscanf( + mac_address_str, + "%x:%x:%x:%x:%x:%x%c", + &values[0], &values[1], + &values[2], &values[3], + &values[4], &values[5], &unexpected) == 6) + { + for (size_t i = 0; i < 6; i++) + { + mac_address->address[i] = (uint8_t)values[i]; + } + result = true; + } + else + { + result = false; + } + } + + return result; +} \ No newline at end of file diff --git a/modules/ble/src/bleio_seq_linux.c b/modules/ble/src/bleio_seq_linux.c new file mode 100644 index 00000000..5063d8fc --- /dev/null +++ b/modules/ble/src/bleio_seq_linux.c @@ -0,0 +1,441 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/refcount.h" + +#include "ble_gatt_io.h" +#include "bleio_seq.h" +#include "bleio_seq_linux_common.h" + +static bool validate_instructions(VECTOR_HANDLE instructions); + +BLEIO_SEQ_HANDLE BLEIO_Seq_Create( + BLEIO_GATT_HANDLE bleio_gatt_handle, + VECTOR_HANDLE instructions, + ON_BLEIO_SEQ_READ_COMPLETE on_read_complete, + ON_BLEIO_SEQ_WRITE_COMPLETE on_write_complete +) +{ + BLEIO_SEQ_HANDLE_DATA* result; + + /*Codes_SRS_BLEIO_SEQ_13_001: [ BLEIO_Seq_Create shall return NULL if bleio_gatt_handle is NULL. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_002: [ BLEIO_Seq_Create shall return NULL if instructions is NULL. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_003: [ BLEIO_Seq_Create shall return NULL if the vector instructions is empty. ]*/ + if( + bleio_gatt_handle == NULL || + instructions == NULL || + VECTOR_size(instructions) == 0 + ) + { + LogError("Invalid input"); + result = NULL; + } + else + { + if (validate_instructions(instructions) == false) + { + result = NULL; + } + else + { + result = REFCOUNT_TYPE_CREATE(BLEIO_SEQ_HANDLE_DATA); + if (result != NULL) + { + /*Codes_SRS_BLEIO_SEQ_13_005: [ BLEIO_Seq_Create shall return a non-NULL handle on successful execution. ]*/ + result->bleio_gatt_handle = bleio_gatt_handle; + result->state = BLEIO_SEQ_STATE_IDLE; + result->instructions = instructions; + result->on_read_complete = on_read_complete; + result->on_write_complete = on_write_complete; + result->on_destroy_complete = NULL; + result->destroy_context = NULL; + } + else + { + /*Codes_SRS_BLEIO_SEQ_13_004: [ BLEIO_Seq_Create shall return NULL if any of the underlying platform calls fail. ]*/ + /* Do nothing. Let control fall through. */ + LogError("malloc failed"); + } + } + } + + return (BLEIO_SEQ_HANDLE)result; +} + +static bool is_write_instruction(BLEIO_SEQ_INSTRUCTION* instruction) +{ + return + ( + instruction->instruction_type == WRITE_AT_INIT || + instruction->instruction_type == WRITE_AT_EXIT || + instruction->instruction_type == WRITE_ONCE + ); +} + + +static bool validate_instruction(BLEIO_SEQ_INSTRUCTION* instruction) +{ + bool result = true; + + // if the characteristic UUID is NULL/empty then it's an error + if (instruction->characteristic_uuid == NULL || STRING_length(instruction->characteristic_uuid) == 0) + { + /*Codes_SRS_BLEIO_SEQ_13_025: [ BLEIO_Seq_Create shall return NULL if the characteristic_uuid field for any instruction is NULL or empty. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_049: [ BLEIO_Seq_AddInstruction shall return NULL if the characteristic_uuid field for any instruction is NULL or empty. ]*/ + LogError("characteristic_uuid is NULL or empty for instruction."); + result = false; + } + + // if this is a read periodic instruction then interval_in_ms should be greater than zero + else if (instruction->instruction_type == READ_PERIODIC && instruction->data.interval_in_ms == 0) + { + /*Codes_SRS_BLEIO_SEQ_13_023: [ BLEIO_Seq_Create shall return NULL if a READ_PERIODIC instruction's interval_in_ms field is zero. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_047: [ BLEIO_Seq_AddInstruction shall return NULL if a READ_PERIODIC instruction's interval_in_ms field is zero. ]*/ + LogError("Instruction is a READ_PERIODIC instruction but interval is zero."); + result = false; + } + + // if this is a write instruction then buffer should not be NULL + else if ( + is_write_instruction(instruction) + && + instruction->data.buffer == NULL + ) + { + /*Codes_SRS_BLEIO_SEQ_13_024: [ BLEIO_Seq_Create shall return NULL if a WRITE_AT_INIT or a WRITE_AT_EXIT or a WRITE_ONCE instruction has a NULL value in the buffer field. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_048: [ BLEIO_Seq_AddInstruction shall return NULL if a WRITE_AT_INIT or a WRITE_AT_EXIT or a WRITE_ONCE instruction has a NULL value in the buffer field. ]*/ + LogError("Instruction is a write instruction but buffer is NULL."); + result = false; + } + else + { + result = true; + } + + return result; +} + +static bool validate_instructions(VECTOR_HANDLE instructions) +{ + bool result = true; + size_t size = VECTOR_size(instructions); + for (size_t i = 0; i < size; i++) + { + BLEIO_SEQ_INSTRUCTION* instruction = (BLEIO_SEQ_INSTRUCTION*)VECTOR_element(instructions, i); + if (instruction == NULL) + { + LogError("VECTOR_element return NULL for index %zu.", i); + result = false; + break; + } + + if (validate_instruction(instruction) == false) + { + LogError("validate_instruction failed for index %zu.", i); + result = false; + break; + } + } + + return result; +} + +void dec_ref_handle(BLEIO_SEQ_HANDLE_DATA* handle_data) +{ + size_t ref_count = DEC_REF(BLEIO_SEQ_HANDLE_DATA, handle_data); + if (ref_count == DEC_RETURN_ZERO) + { + /*Codes_SRS_BLEIO_SEQ_13_031: [ If on_destroy_complete is not NULL then BLEIO_Seq_Destroy shall invoke on_destroy_complete once all WRITE_AT_EXIT instructions have been executed. ]*/ + // call on_destroy_complete + if (handle_data->on_destroy_complete != NULL) + { + /*Codes_SRS_BLEIO_SEQ_13_032: [ If on_destroy_complete is not NULL then BLEIO_Seq_Destroy shall pass context as-is to on_destroy_complete. ]*/ + handle_data->on_destroy_complete((BLEIO_SEQ_HANDLE)handle_data, handle_data->destroy_context); + } + + /*Codes_SRS_BLEIO_SEQ_13_006: [ BLEIO_Seq_Destroy shall free all resources associated with the handle once all the pending I/O operations are complete. ]*/ + // free the strings and buffers + for (size_t i = 0, len = VECTOR_size(handle_data->instructions); i < len; ++i) + { + BLEIO_SEQ_INSTRUCTION *instruction = (BLEIO_SEQ_INSTRUCTION *)VECTOR_element(handle_data->instructions, i); + if (instruction->characteristic_uuid != NULL) + { + STRING_delete(instruction->characteristic_uuid); + } + + if (is_write_instruction(instruction)) + { + if (instruction->data.buffer != NULL) + { + BUFFER_delete(instruction->data.buffer); + } + } + } + + VECTOR_destroy(handle_data->instructions); + BLEIO_gatt_destroy(handle_data->bleio_gatt_handle); + free(handle_data); + } +} + +void BLEIO_Seq_Destroy( + BLEIO_SEQ_HANDLE bleio_seq_handle, + ON_BLEIO_SEQ_DESTROY_COMPLETE on_destroy_complete, + void* context +) +{ + BLEIO_SEQ_HANDLE_DATA* handle_data = (BLEIO_SEQ_HANDLE_DATA*)bleio_seq_handle; + + /*Codes_SRS_BLEIO_SEQ_13_007: [ If bleio_seq_handle is NULL then BLEIO_Seq_Destroy shall do nothing. ]*/ + if (handle_data != NULL) + { + // save the destroy complete callback + handle_data->on_destroy_complete = on_destroy_complete; + handle_data->destroy_context = context; + + // mark the sequencer as shutting down + handle_data->state = BLEIO_SEQ_STATE_SHUTTING_DOWN; + + // schedule WRITE_AT_EXIT operations + size_t size = VECTOR_size(handle_data->instructions); + for (size_t i = 0; i < size; i++) + { + // this MUST NOT be NULL + BLEIO_SEQ_INSTRUCTION* instruction = (BLEIO_SEQ_INSTRUCTION*)VECTOR_element(handle_data->instructions, i); + if (instruction->instruction_type == WRITE_AT_EXIT) + { + /*Codes_SRS_BLEIO_SEQ_13_011: [ BLEIO_Seq_Destroy shall schedule the execution of all WRITE_AT_EXIT instructions.*/ + if (schedule_write(handle_data, instruction, NULL) != BLEIO_SEQ_OK) + { + LogError("Scheduling WRITE_AT_EXIT instruction at index %zu failed.", i); + } + } + } + + // decrement the reference + dec_ref_handle(handle_data); + } + else + { + LogError("bleio_seq_handle is NULL"); + } +} + +static BLEIO_SEQ_RESULT schedule_instruction( + BLEIO_SEQ_HANDLE_DATA* handle_data, + BLEIO_SEQ_INSTRUCTION* instruction, + ON_INTERNAL_IO_COMPLETE on_internal_read_complete +) +{ + BLEIO_SEQ_RESULT result; + + switch (instruction->instruction_type) + { + case READ_ONCE: + /*Codes_SRS_BLEIO_SEQ_13_015: [ BLEIO_Seq_Run shall schedule execution of all READ_ONCE instructions. ]*/ + result = schedule_read(handle_data, instruction, on_internal_read_complete); + break; + + case READ_PERIODIC: + result = schedule_periodic(handle_data, instruction, on_internal_read_complete); + break; + + case WRITE_ONCE: + case WRITE_AT_INIT: + /*Codes_SRS_BLEIO_SEQ_13_016: [ BLEIO_Seq_Run shall schedule execution of all WRITE_AT_INIT instructions. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_033: [ BLEIO_Seq_Run shall schedule execution of all WRITE_ONCE instructions. ]*/ + result = schedule_write(handle_data, instruction, on_internal_read_complete); + break; + + case WRITE_AT_EXIT: + if (VECTOR_push_back(handle_data->instructions, instruction, 1) != 0) + { + LogError("VECTOR_push_back failed"); + result = BLEIO_SEQ_ERROR; + } + else + { + result = BLEIO_SEQ_OK; + } + + break; + + default: + LogError("Unknown instruction type"); + result = BLEIO_SEQ_ERROR; + break; + } + + return result; +} + +BLEIO_SEQ_RESULT BLEIO_Seq_Run(BLEIO_SEQ_HANDLE bleio_seq_handle) +{ + BLEIO_SEQ_RESULT result; + + /*Codes_SRS_BLEIO_SEQ_13_010: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if bleio_seq_handle is NULL. ]*/ + if (bleio_seq_handle == NULL) + { + LogError("bleio_seq_handle is NULL"); + result = BLEIO_SEQ_ERROR; + } + else + { + BLEIO_SEQ_HANDLE_DATA* handle_data = (BLEIO_SEQ_HANDLE_DATA*)bleio_seq_handle; + + /*Codes_SRS_BLEIO_SEQ_13_013: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if BLEIO_Seq_Run was previously called on this handle. ]*/ + if (handle_data->state != BLEIO_SEQ_STATE_IDLE) + { + LogError("Sequence should be in state BLEIO_SEQ_STATE_IDLE but was found to be in: %d", handle_data->state); + result = BLEIO_SEQ_ERROR; + } + else + { + // update the state of the sequence; we do this ahead of starting the scheduling + // process because it is possible that an async I/O operation completes execution + // even before the 'schedule_*' function returns in which case the callbacks for + // I/O operation might think that the sequence is in an 'idle' state when it is + // actually 'running' + handle_data->state = BLEIO_SEQ_STATE_RUNNING; + + for (size_t i = 0, size = VECTOR_size(handle_data->instructions); i < size; i++) + { + // this MUST NOT be NULL + BLEIO_SEQ_INSTRUCTION* instruction = (BLEIO_SEQ_INSTRUCTION*)VECTOR_element(handle_data->instructions, i); + + // schedule this instruction only if it is not a WRITE_AT_EXIT instruction + if (instruction->instruction_type != WRITE_AT_EXIT) + { + result = schedule_instruction(handle_data, instruction, NULL); + + // bail if a schedule operation didn't succeed + if (result != BLEIO_SEQ_OK) + { + LogError("An error occurred while scheduling an instruction of type %d for characteristic %s", + instruction->instruction_type, STRING_c_str(instruction->characteristic_uuid)); + break; + } + } + } + } + + // update the state of the sequence again depending on whether the scheduling process + // worked or not + handle_data->state = (result == BLEIO_SEQ_OK) ? BLEIO_SEQ_STATE_RUNNING : BLEIO_SEQ_STATE_ERROR; + } + + return result; +} + +static void on_instruction_complete( + BLEIO_SEQ_HANDLE_DATA* bleio_seq_handle, + BLEIO_SEQ_INSTRUCTION* instruction +) +{ + // free the characteristic UUID + STRING_delete(instruction->characteristic_uuid); + + /** + * NOTE: We don't free the BUFFER_HANDLE for a write instruction + * because the caller of this callback should have done that in + * bleio_seq_linux_schedule_write.c. + */ + + // free the instruction + free(instruction); +} + +BLEIO_SEQ_RESULT BLEIO_Seq_AddInstruction( + BLEIO_SEQ_HANDLE bleio_seq_handle, + BLEIO_SEQ_INSTRUCTION* instruction +) +{ + BLEIO_SEQ_RESULT result; + + /*Codes_SRS_BLEIO_SEQ_13_036: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if bleio_seq_handle is NULL. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_046: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if instruction is NULL. ]*/ + if ( + bleio_seq_handle == NULL || + instruction == NULL || + instruction->characteristic_uuid == NULL || + validate_instruction(instruction) == false + ) + { + LogError("Invalid instruction provided"); + result = BLEIO_SEQ_ERROR; + } + else + { + BLEIO_SEQ_HANDLE_DATA* handle_data = (BLEIO_SEQ_HANDLE_DATA*)bleio_seq_handle; + + /*Codes_SRS_BLEIO_SEQ_13_045: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if BLEIO_Seq_Run was NOT called first. ]*/ + if (handle_data->state != BLEIO_SEQ_STATE_RUNNING) + { + LogError("Sequence should be in state BLEIO_SEQ_STATE_RUNNING but was found to be in: %d", handle_data->state); + result = BLEIO_SEQ_ERROR; + } + else + { + // copy the instruction into a new struct + BLEIO_SEQ_INSTRUCTION* instr = (BLEIO_SEQ_INSTRUCTION*)malloc(sizeof(BLEIO_SEQ_INSTRUCTION)); + if (instr == NULL) + { + /*Codes_SRS_BLEIO_SEQ_13_037: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + LogError("malloc failed"); + result = BLEIO_SEQ_ERROR; + } + else + { + instr->instruction_type = instruction->instruction_type; + instr->characteristic_uuid = instruction->characteristic_uuid; + instr->context = instruction->context; + instr->data = instruction->data; + + // we save the result of this condition in a boolean because after the + // 'schedule_instruction' call below, there is no guarantee that "instr" + // is valid anymore because the instruction might get executed and then + // freed (in 'on_instruction_complete') by the time we hit the next line + // (in case of Linux, if we're using GLib however, our use of GLib loops + // *does* in fact guarantee that nothing will happen in parallel on this + // thread but we might potentially be working with other threading// models) + bool is_write_at_exit_instr = (instr->instruction_type == WRITE_AT_EXIT); + + /*Codes_SRS_BLEIO_SEQ_13_038: [ BLEIO_Seq_AddInstruction shall schedule execution of the instruction. ]*/ + LogInfo("Scheduling a new instruction."); + result = schedule_instruction(handle_data, instr, on_instruction_complete); + if (result != BLEIO_SEQ_OK) + { + free(instr); + LogError("An error occurred while scheduling an instruction of type %d for characteristic %s", + instruction->instruction_type, STRING_c_str(instruction->characteristic_uuid)); + } + else + { + // if this is a WRITE_AT_EXIT instruction then it has been added to the instructions vector + // so that it is run when the sequence is destroyed; so we should free the memory allocated + // for "instr" since the vector maintains its own copy of the instruction + if (is_write_at_exit_instr == true) + { + free(instr); + } + } + } + } + } + + return result; +} diff --git a/modules/ble/src/bleio_seq_linux_schedule_periodic.c b/modules/ble/src/bleio_seq_linux_schedule_periodic.c new file mode 100644 index 00000000..7d89fb67 --- /dev/null +++ b/modules/ble/src/bleio_seq_linux_schedule_periodic.c @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/refcount.h" + +#include "ble_gatt_io.h" +#include "bleio_seq.h" +#include "bleio_seq_linux_common.h" + +typedef struct TIMER_CONTEXT_TAG { + BLEIO_SEQ_HANDLE_DATA* handle_data; + BLEIO_SEQ_INSTRUCTION* instruction; + ON_INTERNAL_IO_COMPLETE on_internal_read_complete; +}TIMER_CONTEXT; + +static gboolean on_timer(gpointer user_data) +{ + TIMER_CONTEXT* context = (TIMER_CONTEXT*)user_data; + + /*Codes_SRS_BLEIO_SEQ_13_009: [ If there are active instructions of type READ_PERIODIC in progress then the timers associated with those instructions shall be cancelled. ]*/ + // we cancel the timer when the sequencer is not in 'running' state + gboolean result = (context->handle_data->state == BLEIO_SEQ_STATE_RUNNING) ? + G_SOURCE_CONTINUE : G_SOURCE_REMOVE; + + if (result == G_SOURCE_CONTINUE) + { + BLEIO_SEQ_RESULT read_result = schedule_read( + context->handle_data, + context->instruction, + NULL + ); + if (read_result == BLEIO_SEQ_ERROR) + { + LogError("An error occurred while scheduling instruction of type %d for characteristic %s", + context->instruction->instruction_type, + STRING_c_str(context->instruction->characteristic_uuid) + ); + + context->handle_data->on_read_complete( + (BLEIO_SEQ_HANDLE)context->handle_data, + context->instruction->context, + STRING_c_str(context->instruction->characteristic_uuid), + context->instruction->instruction_type, + read_result, + NULL + ); + } + } + else + { + // invoke the internal complete callback if we have one + if (context->on_internal_read_complete != NULL) + { + context->on_internal_read_complete(context->handle_data, context->instruction); + } + + // dec ref the handle data + dec_ref_handle(context->handle_data); + free(context); + } + + return result; +} + +BLEIO_SEQ_RESULT schedule_periodic( + BLEIO_SEQ_HANDLE_DATA* handle_data, + BLEIO_SEQ_INSTRUCTION* instruction, + ON_INTERNAL_IO_COMPLETE on_internal_read_complete +) +{ + BLEIO_SEQ_RESULT result; + TIMER_CONTEXT* context = (TIMER_CONTEXT*)malloc(sizeof(TIMER_CONTEXT)); + + /*Codes_SRS_BLEIO_SEQ_13_014: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + if (context == NULL) + { + LogError("malloc failed"); + result = BLEIO_SEQ_ERROR; + } + else + { + context->handle_data = handle_data; + context->instruction = instruction; + context->on_internal_read_complete = on_internal_read_complete; + + // add ref to the handle data object since we now will have an + // outstanding timer running that needs access to it; + // + // the reason why we increment the reference here as opposed to when + // we know that 'g_timeout_add' was successful is because + // the first timer callback could occur even before we hit the if + // check after this call and 'on-timer' might have run by then + // in which case it would have done a DEC_REF and the ref counts will be + // out of whack + INC_REF(BLEIO_SEQ_HANDLE_DATA, handle_data); + + /*Codes_SRS_BLEIO_SEQ_13_017: [ BLEIO_Seq_Run shall create timers at the specified intervals for scheduling execution of all READ_PERIODIC instructions. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_039: [ BLEIO_Seq_AddInstruction shall create a timer at the specified interval if the instruction is a READ_PERIODIC instruction. ]*/ + guint timer_id = g_timeout_add( + instruction->data.interval_in_ms, + on_timer, + context + ); + if (timer_id == 0) + { + LogError("g_timeout_add failed"); + DEC_REF(BLEIO_SEQ_HANDLE_DATA, handle_data); + free(context); + result = BLEIO_SEQ_ERROR; + } + else + { + result = BLEIO_SEQ_OK; + } + } + + return result; +} diff --git a/modules/ble/src/bleio_seq_linux_schedule_read.c b/modules/ble/src/bleio_seq_linux_schedule_read.c new file mode 100644 index 00000000..e8bef17c --- /dev/null +++ b/modules/ble/src/bleio_seq_linux_schedule_read.c @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/refcount.h" + +#include "ble_gatt_io.h" +#include "bleio_seq.h" +#include "bleio_seq_linux_common.h" + +typedef struct READ_CONTEXT_TAG { + BLEIO_SEQ_HANDLE_DATA* handle_data; + BLEIO_SEQ_INSTRUCTION* instruction; + ON_INTERNAL_IO_COMPLETE on_internal_read_complete; +}READ_CONTEXT; + +static void on_read_complete( + BLEIO_GATT_HANDLE bleio_gatt_handle, + void* read_context, + BLEIO_GATT_RESULT result, + const unsigned char* data, + size_t size +) +{ + // this MUST NOT be NULL + READ_CONTEXT* context = (READ_CONTEXT*)read_context; + if (context->handle_data->on_read_complete != NULL) + { + if (result != BLEIO_GATT_OK) + { + LogError("An error occurred while executing instruction of type %d for characteristic %s", + context->instruction->instruction_type, + STRING_c_str(context->instruction->characteristic_uuid) + ); + } + + BUFFER_HANDLE buffer = (result == BLEIO_GATT_OK) ? BUFFER_create(data, size) : NULL; + if (buffer == NULL) + { + LogError("BUFFER_create failed."); + } + + /*Codes_SRS_BLEIO_SEQ_13_018: [ When a READ_ONCE or a READ_PERIODIC instruction completes execution this API shall invoke the on_read_complete callback passing in the data that was read along with the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure.]*/ + /*Codes_SRS_BLEIO_SEQ_13_040: [ When a READ_ONCE or a READ_PERIODIC instruction completes execution this API shall invoke the on_read_complete callback passing in the data that was read along with the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + context->handle_data->on_read_complete( + (BLEIO_SEQ_HANDLE)(context->handle_data), + context->instruction->context, + STRING_c_str(context->instruction->characteristic_uuid), + context->instruction->instruction_type, + (result == BLEIO_GATT_OK && buffer != NULL) ? BLEIO_SEQ_OK : BLEIO_SEQ_ERROR, + buffer + ); + } + + // invoke the internal complete callback if we have one + if (context->on_internal_read_complete != NULL) + { + context->on_internal_read_complete(context->handle_data, context->instruction); + } + + // dec ref the handle data + dec_ref_handle(context->handle_data); + free(context); +} + +BLEIO_SEQ_RESULT schedule_read( + BLEIO_SEQ_HANDLE_DATA* handle_data, + BLEIO_SEQ_INSTRUCTION* instruction, + ON_INTERNAL_IO_COMPLETE on_internal_read_complete +) +{ + BLEIO_SEQ_RESULT result; + READ_CONTEXT* context = (READ_CONTEXT*)malloc(sizeof(READ_CONTEXT)); + + /*Codes_SRS_BLEIO_SEQ_13_014: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + if (context == NULL) + { + LogError("malloc failed"); + result = BLEIO_SEQ_ERROR; + } + else + { + context->handle_data = handle_data; + context->instruction = instruction; + context->on_internal_read_complete = on_internal_read_complete; + + // add ref to the handle data object since we now will have an + // outstanding I/O operation; the reason why we increment the + // reference here as opposed to when we know that BLEIO_gatt_read_char_by_uuid + // was successful is because the operation could potentially complete + // even before we hit the if check after this call and 'on_read_complete' + // might have run by then in which case it would have done a DEC_REF and + // the ref counts will be out of whack + INC_REF(BLEIO_SEQ_HANDLE_DATA, handle_data); + + int read_result = BLEIO_gatt_read_char_by_uuid( + handle_data->bleio_gatt_handle, + STRING_c_str(instruction->characteristic_uuid), + on_read_complete, + context + ); + + if (read_result != 0) + { + /*Codes_SRS_BLEIO_SEQ_13_014: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + result = BLEIO_SEQ_ERROR; + free(context); + DEC_REF(BLEIO_SEQ_HANDLE_DATA, handle_data); + LogError("BLEIO_gatt_read_char_by_uuid failed with %d.", read_result); + } + else + { + result = BLEIO_SEQ_OK; + } + } + + return result; +} diff --git a/modules/ble/src/bleio_seq_linux_schedule_write.c b/modules/ble/src/bleio_seq_linux_schedule_write.c new file mode 100644 index 00000000..2cc8dfa8 --- /dev/null +++ b/modules/ble/src/bleio_seq_linux_schedule_write.c @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/refcount.h" + +#include "ble_gatt_io.h" +#include "bleio_seq.h" +#include "bleio_seq_linux_common.h" + +typedef struct WRITE_CONTEXT_TAG { + BLEIO_SEQ_HANDLE_DATA* handle_data; + BLEIO_SEQ_INSTRUCTION* instruction; + ON_INTERNAL_IO_COMPLETE on_internal_read_complete; +}WRITE_CONTEXT; + +static void on_write_complete(BLEIO_GATT_HANDLE bleio_gatt_handle, void* write_context, BLEIO_GATT_RESULT result) +{ + // this MUST NOT be NULL + WRITE_CONTEXT* context = (WRITE_CONTEXT*)write_context; + if (context->handle_data->on_write_complete != NULL) + { + if (result != BLEIO_GATT_OK) + { + LogError("An error occurred while executing instruction of type %d for characteristic %s", + context->instruction->instruction_type, + STRING_c_str(context->instruction->characteristic_uuid) + ); + } + + /*Codes_SRS_BLEIO_SEQ_13_021: [ When the WRITE_AT_EXIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_020: [ When the WRITE_AT_INIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_034: [ When the WRITE_ONCE instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_042: [ When a WRITE_ONCE or a WRITE_AT_INIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + context->handle_data->on_write_complete( + (BLEIO_SEQ_HANDLE)context->handle_data, + context->instruction->context, + STRING_c_str(context->instruction->characteristic_uuid), + context->instruction->instruction_type, + result == BLEIO_GATT_OK ? BLEIO_SEQ_OK : BLEIO_SEQ_ERROR + ); + } + + /*Codes_SRS_BLEIO_SEQ_13_026: [ When the WRITE_AT_INIT instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_041: [ When a WRITE_AT_INIT or a WRITE_ONCE instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_027: [ When the WRITE_AT_EXIT instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + /*Codes_SRS_BLEIO_SEQ_13_035: [ When the WRITE_ONCE instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + // free the buffer that was written + BUFFER_delete(context->instruction->data.buffer); + context->instruction->data.buffer = NULL; + + // invoke the internal complete callback if we have one + if (context->on_internal_read_complete != NULL) + { + context->on_internal_read_complete(context->handle_data, context->instruction); + } + + // dec ref the handle data + // NOTE: The call below MUST occur *after* the BUFFER_delete call above + // because `dec_ref_handle` might end up destroying the sequence itself + // at which point context->instruction no longer exists (because it is + // simply a pointer to the instructions vector in the sequence). + dec_ref_handle(context->handle_data); + + free(context); +} + +BLEIO_SEQ_RESULT schedule_write( + BLEIO_SEQ_HANDLE_DATA* handle_data, + BLEIO_SEQ_INSTRUCTION* instruction, + ON_INTERNAL_IO_COMPLETE on_internal_read_complete +) +{ + BLEIO_SEQ_RESULT result; + WRITE_CONTEXT* context = (WRITE_CONTEXT*)malloc(sizeof(WRITE_CONTEXT)); + + /*Codes_SRS_BLEIO_SEQ_13_014: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + if (context == NULL) + { + LogError("malloc failed"); + result = BLEIO_SEQ_ERROR; + } + else + { + context->handle_data = handle_data; + context->instruction = instruction; + context->on_internal_read_complete = on_internal_read_complete; + + const unsigned char* buffer = BUFFER_u_char(instruction->data.buffer); + size_t buffer_size = BUFFER_length(instruction->data.buffer); + + // add ref to the handle data object since we now will have an + // outstanding I/O operation; the reason why we increment the + // reference here as opposed to when we know that BLEIO_gatt_read_char_by_uuid + // was successful is because the operation could potentially complete + // even before we hit the if check after this call and 'on_read_complete' + // might have run by then in which case it would have done a DEC_REF and + // the ref counts will be out of whack + INC_REF(BLEIO_SEQ_HANDLE_DATA, handle_data); + + int write_result = BLEIO_gatt_write_char_by_uuid( + handle_data->bleio_gatt_handle, + STRING_c_str(instruction->characteristic_uuid), + buffer, + buffer_size, + on_write_complete, + context + ); + if (write_result != 0) + { + /*Codes_SRS_BLEIO_SEQ_13_014: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + result = BLEIO_SEQ_ERROR; + free(context); + DEC_REF(BLEIO_SEQ_HANDLE_DATA, handle_data); + LogError("BLEIO_gatt_write_char_by_uuid failed with %d.", write_result); + } + else + { + result = BLEIO_SEQ_OK; + } + } + + return result; +} diff --git a/modules/ble/src/bleio_seq_windows.c b/modules/ble/src/bleio_seq_windows.c new file mode 100644 index 00000000..f143dc1b --- /dev/null +++ b/modules/ble/src/bleio_seq_windows.c @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" + +#include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/buffer_.h" +#include "ble_gatt_io.h" +#include "bleio_seq.h" + +BLEIO_SEQ_HANDLE BLEIO_Seq_Create( + BLEIO_GATT_HANDLE bleio_gatt_handle, + VECTOR_HANDLE instructions, + ON_BLEIO_SEQ_READ_COMPLETE on_read_complete, + ON_BLEIO_SEQ_WRITE_COMPLETE on_write_complete +) +{ + /*Codes_SRS_BLEIO_SEQ_13_028: [On Windows, this function shall return NULL.]*/ + LogError("BLEIO_Seq_Create not implemented on Windows yet."); + return NULL; +} + +void BLEIO_Seq_Destroy( + BLEIO_SEQ_HANDLE bleio_seq_handle, + ON_BLEIO_SEQ_DESTROY_COMPLETE on_destroy_complete, + void* context +) +{ + /*Codes_SRS_BLEIO_SEQ_13_029: [ On Windows, this function shall do nothing. ]*/ + LogError("BLEIO_Seq_Destroy not implemented on Windows yet."); +} + +BLEIO_SEQ_RESULT BLEIO_Seq_Run(BLEIO_SEQ_HANDLE bleio_seq_handle) +{ + /*Codes_SRS_BLEIO_SEQ_13_030: [ On Windows this function shall return BLEIO_SEQ_ERROR. ]*/ + LogError("BLEIO_Seq_Run not implemented on Windows yet."); + return BLEIO_SEQ_ERROR; +} + +BLEIO_SEQ_RESULT BLEIO_Seq_AddInstruction( + BLEIO_SEQ_HANDLE bleio_seq_handle, + BLEIO_SEQ_INSTRUCTION* instruction +) +{ + /*Codes_SRS_BLEIO_SEQ_13_044: [ On Windows this function shall return BLEIO_SEQ_ERROR. ]*/ + return BLEIO_SEQ_ERROR; +} \ No newline at end of file diff --git a/modules/ble/src/gio_async_seq.c b/modules/ble/src/gio_async_seq.c new file mode 100644 index 00000000..06846170 --- /dev/null +++ b/modules/ble/src/gio_async_seq.c @@ -0,0 +1,482 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include + +#include +#include + +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/gballoc.h" +#include "gio_async_seq.h" + +#define GIO_ASYNC_SEQ_ERROR_DOMAIN ((GQuark)0x42) + +#define REPORT_ERROR(async_seq, message) \ + do \ + { \ + LogError(message "."); \ + report_error(async_seq, message); \ + } while(0) + +typedef struct GIO_ASYNCSEQ_HANDLE_DATA_TAG +{ + gpointer async_seq_context; // callback context for the entire sequence + GPtrArray* callbacks_list; // list of callbacks to be called in sequence + GIO_ASYNCSEQ_ERROR_CALLBACK error_callback; // error callback + GIO_ASYNCSEQ_COMPLETE_CALLBACK complete_callback; // called when the entire sequence completes successfully + guint32 current_callback_index; // specifies current callback index as sequence runs + GIO_ASYNCSEQ_STATE state; // current state of the sequence +}GIO_ASYNCSEQ_HANDLE_DATA; + +typedef struct GIO_ASYNCSEQ_CALLBACK_DATA_TAG +{ + GIO_ASYNCSEQ_CALLBACK callback; // callback to be invoked + GIO_ASYNCSEQ_FINISH_CALLBACK finish_callback; // callback to be invoked for finishing an + // async call and fetching the result/error + gpointer callback_context; // context to be supplied for this callback +}GIO_ASYNCSEQ_CALLBACK_DATA; + +static void report_error( + GIO_ASYNCSEQ_HANDLE_DATA* async_seq, + const gchar* message +); + +static void resolve_callback( + GObject* source_object, + GAsyncResult* result, + gpointer user_data +); + +GIO_ASYNCSEQ_HANDLE GIO_Async_Seq_Create( + gpointer async_seq_context, + GIO_ASYNCSEQ_ERROR_CALLBACK error_callback, + GIO_ASYNCSEQ_COMPLETE_CALLBACK complete_callback +) +{ + GIO_ASYNCSEQ_HANDLE_DATA* result = (GIO_ASYNCSEQ_HANDLE_DATA*)malloc( + sizeof(GIO_ASYNCSEQ_HANDLE_DATA) + ); + if (result != NULL) + { + result->callbacks_list = g_ptr_array_new_with_free_func(free); + if (result->callbacks_list != NULL) + { + /*Codes_SRS_GIO_ASYNCSEQ_13_001: [ GIO_Async_Seq_Create shall return a non-NULL handle on successful execution. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_005: [GIO_Async_Seq_Create shall save the async_seq_context pointer so that it can be retrieved later by calling GIO_Async_Seq_GetContext. ] */ + result->async_seq_context = async_seq_context; + result->error_callback = error_callback; + result->complete_callback = complete_callback; + result->current_callback_index = 0; + result->state = GIO_ASYNCSEQ_STATE_PENDING; + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_002: [ GIO_Async_Seq_Create shall return NULL when any of the underlying platform calls fail. ]*/ + LogError("g_ptr_array_new() returned NULL"); + free(result); + result = NULL; + } + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_002: [ GIO_Async_Seq_Create shall return NULL when any of the underlying platform calls fail. ]*/ + LogError("malloc() returned NULL"); + /* Fall through. 'result' is NULL. */ + } + + return (GIO_ASYNCSEQ_HANDLE)result; +} + +void GIO_Async_Seq_Destroy(GIO_ASYNCSEQ_HANDLE async_seq_handle) +{ + /* + * NOTE: We *DO NOT* attempt to cancel pending I/O operations if any are + * in progress here. + */ + if (async_seq_handle != NULL) + { + GIO_ASYNCSEQ_HANDLE_DATA* async_seq = (GIO_ASYNCSEQ_HANDLE_DATA*)async_seq_handle; + if (async_seq->state == GIO_ASYNCSEQ_STATE_RUNNING) + { + LogError("Async sequence is still running. Destroying anyway."); + } + + /*Codes_SRS_GIO_ASYNCSEQ_13_004: [ GIO_Async_Seq_Destroy shall free all resources associated with the handle. ]*/ + g_ptr_array_unref(async_seq->callbacks_list); // automatically calls 'free' + // for every entry in the array + free(async_seq); + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_003: [ GIO_Async_Seq_Destroy shall do nothing if async_seq_handle is NULL. ]*/ + LogError("async_seq_handle is NULL."); + } +} + +gpointer GIO_Async_Seq_GetContext(GIO_ASYNCSEQ_HANDLE async_seq_handle) +{ + gpointer result; + if (async_seq_handle != NULL) + { + GIO_ASYNCSEQ_HANDLE_DATA* async_seq = (GIO_ASYNCSEQ_HANDLE_DATA*)async_seq_handle; + + /*Codes_SRS_GIO_ASYNCSEQ_13_007: [ GIO_Async_Seq_GetContext shall return the value of the async_seq_context parameter that was passed in when calling GIO_Async_Seq_Create. ]*/ + result = async_seq->async_seq_context; + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_006: [ GIO_Async_Seq_GetContext shall return NULL if async_seq_handle is NULL. ]*/ + LogError("async_seq_handle is NULL."); + result = NULL; + } + + return result; +} + +/** + * The varargs part of this API is a sequence of parameter pairs, where each + * pair is a GIO_ASYNCSEQ_CALLBACK pointer followed by a GIO_ASYNCSEQ_FINISH_CALLBACK + * pointer. For e.g., + * + * GIO_Async_Seq_Add(async_seq_handle, NULL, + * on_get_bus, on_get_bus_finish, + * on_get_object_manager, on_get_object_manager_finish, + * on_get_device, on_get_device_finish, + * NULL + * ); + */ +static GIO_ASYNCSEQ_RESULT seq_add_internal( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer callback_context, + va_list args_list +) +{ + GIO_ASYNCSEQ_RESULT result; + GIO_ASYNCSEQ_HANDLE_DATA* async_seq = (GIO_ASYNCSEQ_HANDLE_DATA*)async_seq_handle; + + // keep track of the end of the callbacks list before anything is added to it + guint length_before = async_seq->callbacks_list->len; + + // the args list is a NULL terminated sequence of + // (GIO_ASYNCSEQ_CALLBACK, GIO_ASYNCSEQ_FINISH_CALLBACK) pointer tuples + GIO_ASYNCSEQ_CALLBACK current_callback = va_arg(args_list, GIO_ASYNCSEQ_CALLBACK); + + /*Codes_SRS_GIO_ASYNCSEQ_13_011: [ GIO_Async_Seq_Add shall add callbacks from the variable arguments list till a callback whose value is NULL is encountered. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_038: [ GIO_Async_Seq_Addv shall add callbacks from the variable arguments list till a callback whose value is NULL is encountered. ]*/ + while (current_callback != NULL) + { + GIO_ASYNCSEQ_FINISH_CALLBACK finish_callback = va_arg( + args_list, + GIO_ASYNCSEQ_FINISH_CALLBACK + ); + GIO_ASYNCSEQ_CALLBACK_DATA* callback_data = NULL; + + if ( + /*Codes_SRS_GIO_ASYNCSEQ_13_012: [ When a GIO_ASYNCSEQ_CALLBACK is encountered in the varargs, the next argument MUST be non-NULL. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_039: [ When a GIO_ASYNCSEQ_CALLBACK is encountered in the varargs, the next argument MUST be non-NULL. ]*/ + // the finish callback MUST NOT be NULL + finish_callback != NULL && + ( + callback_data = (GIO_ASYNCSEQ_CALLBACK_DATA*)malloc( + sizeof(GIO_ASYNCSEQ_CALLBACK_DATA) + ) + ) != NULL + ) + { + callback_data->callback = current_callback; + callback_data->finish_callback = finish_callback; + + /*Codes_SRS_GIO_ASYNCSEQ_13_014: [ The callback_context pointer value shall be saved so that they can be passed to the callbacks later when they are invoked. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_041: [ The callback_context pointer value shall be saved so that they can be passed to the callbacks later when they are invoked. ]*/ + callback_data->callback_context = callback_context; + + /*Codes_SRS_GIO_ASYNCSEQ_13_010: [ GIO_Async_Seq_Add shall append the new async operations to the end of the existing list of async operations if any. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_037: [ GIO_Async_Seq_Addv shall append the new async operations to the end of the existing list of async operations if any. ]*/ + g_ptr_array_add(async_seq->callbacks_list, callback_data); + } + else + { + LogError("malloc() returned NULL or no finish callback pair found."); + + // since we are bailing remove all the callbacks that were added + // to the list so far; we want this function to be transactional with + // respect to the callbacks added to the callbacks list + if (length_before < async_seq->callbacks_list->len) + { + /*Codes_SRS_GIO_ASYNCSEQ_13_015: [ The list of callbacks that had been added to the sequence before calling this API will remain unchanged after the function returns when an error occurs. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_042: [ The list of callbacks that had been added to the sequence before calling this API will remain unchanged after the function returns when an error occurs. ]*/ + g_ptr_array_remove_range( + async_seq->callbacks_list, + length_before, + async_seq->callbacks_list->len - length_before + ); + } + + // if we are here because of a missing finish callback then the + // malloc would have happened; so we free it + if (callback_data != NULL) + { + free(callback_data); + } + + /*Codes_SRS_GIO_ASYNCSEQ_13_013: [ GIO_Async_Seq_Add shall return GIO_ASYNCSEQ_ERROR when any of the underlying platform calls fail. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_040: [ GIO_Async_Seq_Addv shall return GIO_ASYNCSEQ_ERROR when any of the underlying platform calls fail. ]*/ + result = GIO_ASYNCSEQ_ERROR; + break; + } + + current_callback = va_arg(args_list, GIO_ASYNCSEQ_CALLBACK); + } + + if (current_callback == NULL) + { + /*Codes_SRS_GIO_ASYNCSEQ_13_016: [ GIO_Async_Seq_Add shall return GIO_ASYNCSEQ_OK if the API executes successfully. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_043: [ GIO_Async_Seq_Addv shall return GIO_ASYNCSEQ_OK if the API executes successfully. ]*/ + result = GIO_ASYNCSEQ_OK; + } + + return result; +} + +extern GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Addv( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer callback_context, + va_list args_list +) +{ + GIO_ASYNCSEQ_RESULT result; + if (async_seq_handle != NULL) + { + GIO_ASYNCSEQ_HANDLE_DATA* async_seq = (GIO_ASYNCSEQ_HANDLE_DATA*)async_seq_handle; + + if (async_seq->state == GIO_ASYNCSEQ_STATE_PENDING) + { + result = seq_add_internal(async_seq_handle, callback_context, args_list); + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_036: [ GIO_Async_Seq_Addv shall return GIO_ASYNCSEQ_ERROR if the async sequence's state is not equal to GIO_ASYNCSEQ_STATE_PENDING. ]*/ + LogError("Async sequence must be in the 'pending' state when adding callbacks."); + result = GIO_ASYNCSEQ_ERROR; + } + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_035: [ GIO_Async_Seq_Addv shall return GIO_ASYNCSEQ_ERROR if async_seq_handle is NULL. ]*/ + LogError("async_seq_handle is NULL."); + result = GIO_ASYNCSEQ_ERROR; + } + + return result; +} + +extern GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Add( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer callback_context, + ... +) +{ + GIO_ASYNCSEQ_RESULT result; + if (async_seq_handle != NULL) + { + GIO_ASYNCSEQ_HANDLE_DATA* async_seq = (GIO_ASYNCSEQ_HANDLE_DATA*)async_seq_handle; + + if (async_seq->state == GIO_ASYNCSEQ_STATE_PENDING) + { + va_list args_list; + va_start(args_list, callback_context); + result = seq_add_internal(async_seq_handle, callback_context, args_list); + va_end(args_list); + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_009: [ GIO_Async_Seq_Add shall return GIO_ASYNCSEQ_ERROR if the async sequence's state is not equal to GIO_ASYNCSEQ_STATE_PENDING. ]*/ + LogError("Async sequence must be in the 'pending' state when adding callbacks."); + result = GIO_ASYNCSEQ_ERROR; + } + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_008: [ GIO_Async_Seq_Add shall return GIO_ASYNCSEQ_ERROR if async_seq_handle is NULL. ]*/ + LogError("async_seq_handle is NULL."); + result = GIO_ASYNCSEQ_ERROR; + } + + return result; +} + +GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Run_Async(GIO_ASYNCSEQ_HANDLE async_seq_handle) +{ + GIO_ASYNCSEQ_RESULT result; + + if (async_seq_handle != NULL) + { + GIO_ASYNCSEQ_HANDLE_DATA* async_seq = (GIO_ASYNCSEQ_HANDLE_DATA*)async_seq_handle; + + // sequence must not already be running + if (async_seq->state != GIO_ASYNCSEQ_STATE_RUNNING) + { + // we start running from the first callback + async_seq->current_callback_index = 0; + async_seq->state = GIO_ASYNCSEQ_STATE_RUNNING; + + if (async_seq->callbacks_list->len > 0) + { + GIO_ASYNCSEQ_CALLBACK_DATA* first_callback_data = + (GIO_ASYNCSEQ_CALLBACK_DATA*)g_ptr_array_index( + async_seq->callbacks_list, 0 + ); + + /*Codes_SRS_GIO_ASYNCSEQ_13_020: [ GIO_Async_Seq_Run shall invoke the 'start' callback of the first async operation in the sequence. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_021: [ GIO_Async_Seq_Run shall pass the callback context that was supplied to GIO_Async_Seq_Add when the first operation was added to the sequence when invoking the 'start' callback. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_023: [ GIO_Async_Seq_Run shall supply a non-NULL pointer to a function as the value of the async_callback parameter when calling the 'start' callback. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_031: [GIO_Async_Seq_Run_Async shall supply NULL as the value of the previous_result parameter of the 'start' callback. ]*/ + // kick-off sequence + first_callback_data->callback( + async_seq_handle, + NULL, // no "previous" result for first callback + first_callback_data->callback_context, + resolve_callback + ); + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_019: [ GIO_Async_Seq_Run shall complete the sequence and invoke the sequence's complete callback if there are no asynchronous operations to process. ]*/ + // this is an empty sequence, so we are done + LogInfo("Async sequence is empty."); + async_seq->state = GIO_ASYNCSEQ_STATE_COMPLETE; + if (async_seq->complete_callback != NULL) + { + async_seq->complete_callback(async_seq_handle, NULL); + } + } + + /*Codes_SRS_GIO_ASYNCSEQ_13_022: [ GIO_Async_Seq_Run shall return GIO_ASYNCSEQ_OK when there are no errors. ]*/ + result = GIO_ASYNCSEQ_OK; + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_018: [ GIO_Async_Seq_Run shall return GIO_ASYNCSEQ_ERROR if the sequence's state is already GIO_ASYNCSEQ_STATE_RUNNING. ]*/ + result = GIO_ASYNCSEQ_ERROR; + LogError("Async sequence is already running."); + } + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_017: [ GIO_Async_Seq_Run shall return GIO_ASYNCSEQ_ERROR if async_seq_handle is NULL. ]*/ + result = GIO_ASYNCSEQ_ERROR; + LogError("async_seq_handle is NULL."); + } + + return result; +} + +static void resolve_callback( + GObject* source_object, + GAsyncResult* result, + gpointer user_data +) +{ + GIO_ASYNCSEQ_HANDLE_DATA* async_seq = (GIO_ASYNCSEQ_HANDLE_DATA*)user_data; + // 'user_data' MUST be a GIO_ASYNCSEQ_HANDLE_DATA pointer + if (user_data != NULL) + { + // sequence must be in 'running' state + if (async_seq->state == GIO_ASYNCSEQ_STATE_RUNNING) + { + // get the current callback data & finish the async call + GIO_ASYNCSEQ_CALLBACK_DATA* callback_data = (GIO_ASYNCSEQ_CALLBACK_DATA*)g_ptr_array_index( + async_seq->callbacks_list, + async_seq->current_callback_index + ); + + /*Codes_SRS_GIO_ASYNCSEQ_13_026: [ resolve_callback shall invoke the 'finish' callback of the async operation that was just concluded. ]*/ + GError* error = NULL; + gpointer result_data = callback_data->finish_callback( + (GIO_ASYNCSEQ_HANDLE)async_seq, result, &error + ); + + // handle error + if (error != NULL) + { + /*Codes_SRS_GIO_ASYNCSEQ_13_027: [ resolve_callback shall invoke the sequence's error callback and suspend execution of the sequence if the 'finish' callback returns a non-NULL GError pointer. ]*/ + async_seq->state = GIO_ASYNCSEQ_STATE_ERROR; + if (async_seq->error_callback != NULL) + { + async_seq->error_callback((GIO_ASYNCSEQ_HANDLE)async_seq, error); + } + + /*Codes_SRS_GIO_ASYNCSEQ_13_028: [ resolve_callback shall free the GError object by calling g_clear_error if the 'finish' callback returns a non-NULL GError pointer. ]*/ + g_clear_error(&error); + } + else + { + // invoke next callback in sequence, if any + async_seq->current_callback_index = async_seq->current_callback_index + 1; + if (async_seq->current_callback_index < async_seq->callbacks_list->len) + { + GIO_ASYNCSEQ_CALLBACK_DATA* next_callback_data = + (GIO_ASYNCSEQ_CALLBACK_DATA*)g_ptr_array_index( + async_seq->callbacks_list, + async_seq->current_callback_index + ); + + /*Codes_SRS_GIO_ASYNCSEQ_13_029: [ resolve_callback shall invoke the 'start' callback of the next async operation in the sequence. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_030: [ resolve_callback shall supply the result of calling the 'finish' callback of the current async operation as the value of the previous_result parameter of the 'start' callback of the next async operation. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_032: [ resolve_callback shall pass the callback context that was supplied to GIO_Async_Seq_Add when the next async operation was added to the sequence when invoking the 'start' callback. ]*/ + /*Codes_SRS_GIO_ASYNCSEQ_13_033: [ resolve_callback shall supply a non-NULL pointer to a function as the value of the async_callback parameter when calling the 'start' callback. ]*/ + next_callback_data->callback( + (GIO_ASYNCSEQ_HANDLE)async_seq, + result_data, + next_callback_data->callback_context, + resolve_callback + ); + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_034: [ resolve_callback shall complete the sequence and invoke the sequence's complete callback if there are no more asynchronous operations to process. ]*/ + // sequence is complete + async_seq->state = GIO_ASYNCSEQ_STATE_COMPLETE; + if (async_seq->complete_callback != NULL) + { + async_seq->complete_callback((GIO_ASYNCSEQ_HANDLE)async_seq, result_data); + } + } + } + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_025: [ resolve_callback shall invoke the sequence's error callback and suspend execution of the sequence if the state of the sequence is not equal to GIO_ASYNCSEQ_STATE_RUNNING. ]*/ + async_seq->state = GIO_ASYNCSEQ_STATE_ERROR; + REPORT_ERROR(async_seq, "Async sequence is not in 'running' state"); + } + } + else + { + /*Codes_SRS_GIO_ASYNCSEQ_13_024: [ resolve_callback shall do nothing else if user_data is NULL. ]*/ + LogError("user_data is NULL."); + } +} + +static void report_error( + GIO_ASYNCSEQ_HANDLE_DATA* async_seq, + const gchar* message +) +{ + static GQuark error_domain = GIO_ASYNC_SEQ_ERROR_DOMAIN; + if(async_seq->error_callback != NULL) + { + GError* error = g_error_new_literal( + error_domain, GIO_ASYNCSEQ_ERROR, message + ); + async_seq->error_callback((GIO_ASYNCSEQ_HANDLE)async_seq, error); + g_clear_error(&error); + } +} \ No newline at end of file diff --git a/modules/ble/tests/CMakeLists.txt b/modules/ble/tests/CMakeLists.txt new file mode 100644 index 00000000..4d287854 --- /dev/null +++ b/modules/ble/tests/CMakeLists.txt @@ -0,0 +1,15 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) +#this is CMakeLists for ble tests folder + +add_subdirectory(ble_gatt_io_unittests) +add_subdirectory(bleio_seq_unittests) +add_subdirectory(ble_unittests) +add_subdirectory(ble_hl_unittests) + +# add the gio_async_seq unit tests only for Linux +if(LINUX) + add_subdirectory(gio_async_seq_unittests) +endif() \ No newline at end of file diff --git a/modules/ble/tests/ble_gatt_io_unittests/CMakeLists.txt b/modules/ble/tests/ble_gatt_io_unittests/CMakeLists.txt new file mode 100644 index 00000000..b1262e8a --- /dev/null +++ b/modules/ble/tests/ble_gatt_io_unittests/CMakeLists.txt @@ -0,0 +1,71 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for ble_gatt_io_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName ble_gatt_io_unittests) + +if(LINUX) + set(${theseTestsName}_cpp_files + ${theseTestsName}_linux.cpp + ) + + # Include GIO headers/libs + include_directories(${GWGIOUNIX_INCLUDE_DIRS}) + set(LIBS ${GWGIOUNIX_LIBRARIES}) + + # Blue-z dbus generated sources + set(bluez_headers + ../../deps/linux/dbus-bluez/inc/bluez_characteristic.h + ../../deps/linux/dbus-bluez/inc/bluez_device.h + ) + include_directories(../../deps/linux/dbus-bluez/inc) + + # BLE GATT I/O sources + set(ble_gatt_io_test_sources + ../../src/ble_gatt_io_linux.c + ../../src/ble_gatt_io_linux_connect.c + ../../src/ble_gatt_io_linux_disconnect.c + ../../src/ble_gatt_io_linux_read.c + ../../src/ble_gatt_io_linux_write.c + ) + set(ble_gatt_io_test_headers + ${bluez_headers} + ../../inc/gio_async_seq.h + ../../inc/ble_gatt_io_linux_common.h + ) +elseif(WIN32) + set(${theseTestsName}_cpp_files + ${theseTestsName}_windows.cpp + ) + + set(ble_gatt_io_test_sources + ../../src/ble_gatt_io_windows.c + ) +endif() + +set(ble_gatt_io_test_headers + ${ble_gatt_io_test_headers} + ../../inc/ble_gatt_io.h +) + +include_directories( + ../../inc + ${GW_INC} +) + +set(${theseTestsName}_c_files + ${ble_gatt_io_test_sources} +) + +set(${theseTestsName}_h_files + ${ble_gatt_io_test_headers} +) + +if(WIN32) + build_test_artifacts(${theseTestsName} ON) +else() + build_test_artifacts(${theseTestsName} ON ADDITIONAL_LIBS ${LIBS} VALGRIND_SUPPRESSIONS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../../../../core/valgrind_suppressions.txt) +endif() diff --git a/modules/ble/tests/ble_gatt_io_unittests/ble_gatt_io_unittests_linux.cpp b/modules/ble/tests/ble_gatt_io_unittests/ble_gatt_io_unittests_linux.cpp new file mode 100644 index 00000000..28ca7dbf --- /dev/null +++ b/modules/ble/tests/ble_gatt_io_unittests/ble_gatt_io_unittests_linux.cpp @@ -0,0 +1,3956 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include +#include +#include +#include + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +#include +#include + +#include "gio_async_seq.h" +#include "ble_gatt_io.h" +#include "bluez_device.h" +#include "bluez_characteristic.h" +#include "azure_c_shared_utility/lock.h" + +DEFINE_MICROMOCK_ENUM_TO_STRING(BLEIO_GATT_RESULT, BLEIO_GATT_RESULT_VALUES); + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit + +#include "../../src/gio_async_seq.c" +}; + +#define FILL_UUID(p) \ + for (size_t i = 0; i < sizeof(p) / sizeof(uint8_t); i++) \ + { \ + p[i] = rand() % 256; \ + } \ + + +static void generate_fake_uuid(char* output) +{ + uint8_t part1[4], part2[2], part3[2], part4[2], part5[6]; + FILL_UUID(part1); FILL_UUID(part2); + FILL_UUID(part3); FILL_UUID(part4); FILL_UUID(part5); + sprintf( + output, + "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + part1[0], part1[1], part1[2], part1[3], + part2[0], part2[1], + part3[0], part3[1], + part4[0], part4[1], + part5[0], part5[1], part5[2], part5[3], part5[4], part5[5] + ); +} + +class RefCountObjectBase +{ +public: + size_t ref_count; + +public: + RefCountObjectBase() : + ref_count(1) + { + } + + virtual ~RefCountObjectBase() {} + + size_t inc_ref() + { + return ++ref_count; + } + + void dec_ref() + { + if (--ref_count == 0) + { + delete this; + } + } +}; + +class RefCountObjectFree : public RefCountObjectBase +{ +public: + gpointer object; + +public: + RefCountObjectFree(gpointer obj) : + RefCountObjectBase(), + object(obj) + { + } + + virtual ~RefCountObjectFree() + { + free(object); + } +}; + +template +class RefCountObjectDelete : public RefCountObjectBase +{ +public: + T* object; + +public: + RefCountObjectDelete(T* obj) : + RefCountObjectBase(), + object(obj) + { + } + + virtual ~RefCountObjectDelete() + { + delete object; + } +}; + +class GCompareFuncComparer +{ +private: + GCompareDataFunc _comparer; + gpointer _key_compare_data; + +public: + GCompareFuncComparer(GCompareDataFunc comparer, gpointer key_compare_data) : + _comparer(comparer), + _key_compare_data(_key_compare_data) + {} + + bool operator()(const gpointer& a, const gpointer& b) const + { + // "_comparer" returns: negative value if a < b ; zero if a = b ; positive value if a > b; + // std::map's comparator is essentially a 'less than' operator, so we do the appropriate + // translation + return _comparer(a, b, _key_compare_data) < 0; + } +}; + +// This is a mock implementation of GLIB's map collection - +// GTree - using std::map. +class CGTree +{ +public: + std::map _tree; + GDestroyNotify _key_destroy_func; + GDestroyNotify _value_destroy_func; + +public: + CGTree(GCompareDataFunc key_compare_func, + gpointer key_compare_data, + GDestroyNotify key_destroy_func, + GDestroyNotify value_destroy_func) : + _tree(GCompareFuncComparer(key_compare_func, key_compare_data)), + _key_destroy_func(key_destroy_func), + _value_destroy_func(value_destroy_func) + {} + + ~CGTree() + { + if (_key_destroy_func != NULL || _value_destroy_func != NULL) + { + // invoke the destroy function for all keys and values + for (const auto& it : _tree) + { + if (_key_destroy_func) + { + _key_destroy_func(it.first); + } + + if (_value_destroy_func) + { + _value_destroy_func(it.second); + } + } + } + } + + void insert(gpointer key, gpointer value) + { + bool key_exists = _tree.find(key) != _tree.end(); + if (key_exists && _value_destroy_func) + { + // if a value with this key already exists in the tree then + // destroy it by calling _value_destroy_func on it + _value_destroy_func(_tree[key]); + } + + _tree[key] = value; + + if (key_exists && _key_destroy_func) + { + // if a value with this key already exists in the tree then + // destroy the new key passed in by calling _key_destroy_func on it + _key_destroy_func(key); + } + } + + const gpointer lookup(const gpointer key) const + { + auto it = _tree.find(key); + return it == _tree.end() ? NULL : it->second; + } +}; + +/* +This is a mock implementation of GLib's GList data structure. Only +provides implementations for the following functions: + g_list_find_custom + g_list_foreach + g_list_free +*/ +class CGList +{ +public: + gpointer data; + GList *next; + GList *prev; + +public: + CGList(gpointer d) : + data(d), next(NULL), prev(NULL) + {} + + CGList(gpointer d, GList* n, GList* p) : + data(d), next(n), prev(p) + {} + + ~CGList() + { + // free the next node (it's destructor should + // recursively free all other nodes) + CGList* next = (CGList*)this->next; + if (next) + { + delete next; + } + } + + void append(gpointer d) + { + // navigate to end of list + GList* current = (GList*)this; + while (current->next != NULL) + { + current = current->next; + } + + // create new node and add to the end + current->next = (GList*)(new CGList(d, NULL, current)); + } + + GList* find_custom(gconstpointer user_data, GCompareFunc func) + { + GList* current = (GList*)this; + gint result = -1; + do + { + result = func(current->data, user_data); + } while (result != 0 && (current = current->next) != NULL); + + return current; + } + + void foreach(GFunc func, gpointer user_data) + { + GList* current = (GList*)this; + do + { + func(current->data, user_data); + current = current->next; + } while (current != NULL); + } +}; + +class CGBytes +{ +public: + gpointer _data; + gsize _size; + +public: + CGBytes(gconstpointer data, gsize size) + { + _data = NULL; + _size = size; + if (_size > 0) + { + _data = malloc(_size); + memcpy(_data, data, _size); + } + } + + ~CGBytes() + { + free(_data); + } + + gconstpointer get_data(gsize* size) + { + if (size != NULL) + { + *size = _size; + } + + return _data; + } +}; + +class CGVariant +{ +public: + enum Type + { + string, + bytes, + fixed_array + }; + + struct store_t + { + Type type; + union + { + GString* str; + RefCountObjectDelete* bytes; + CGBytes* fixed_array; + } data; + }store; + + CGVariant(const gchar* str) + { + store.type = CGVariant::string; + store.data.str = g_string_new(str); + } + + CGVariant(RefCountObjectDelete* bytes) + { + store.type = CGVariant::bytes; + store.data.bytes = bytes; + bytes->inc_ref(); + } + + CGVariant(CGBytes* fixed_array) + { + store.type = CGVariant::fixed_array; + store.data.fixed_array = fixed_array; + } + + ~CGVariant() + { + if (store.type == CGVariant::string && store.data.str != NULL) + { + g_string_free(store.data.str, TRUE); + } + else if (store.type == CGVariant::bytes && store.data.bytes != NULL) + { + store.data.bytes->dec_ref(); + } + else if (store.type == CGVariant::fixed_array && store.data.fixed_array != NULL) + { + delete store.data.fixed_array; + } + } +}; + +class DBUSInterface +{ +public: + std::string name; + + DBUSInterface(const std::string& iname) : + name(iname) + {} +}; + +class DBUSObject +{ +public: + std::string object_path; + std::vector interfaces_supported; + + DBUSObject(const std::string& path, const std::vector& interfaces) : + object_path(path), + interfaces_supported(interfaces) + {} + + DBUSObject(const DBUSObject& rhs) : + object_path(rhs.object_path), + interfaces_supported(rhs.interfaces_supported) + {} +}; + +static DBUSObject g_dbus_objects[] = +{ + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service000c/char001b", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0061/char0062", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0051/char0052/desc0054", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service004c", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service000c", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", + { + { "org.bluez.Device1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0027/char0028/desc002a", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0059", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service000c/char000f", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0008/char0009", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service002f/char0033", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", + { + { "org.bluez.Device1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0051", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0027/char002b", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service000c/char000d", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service001f/char0025", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service001f/char0023", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0", + { + { "org.bluez.NetworkServer1" }, + { "org.bluez.Media1" }, + { "org.bluez.GattManager1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.Adapter1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service001f/char0020", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0027/char002d", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service000c/char0015", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service002f/char0030/desc0032", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service000c/char0019", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service003f", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service000c/char0017", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service000c/char0013", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service002f/char0035", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0061/char0066/desc0069", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0061/char0066/desc0068", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service000c/char0011", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service002f/char0030", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0047", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0047/char0048/desc004b", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0047/char0048/desc004a", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0008", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service001f/char0020/desc0022", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0037/char003d", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0037/char003b", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0008/char0009/desc000b", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service003f/char0045", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service002f", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service003f/char0043", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service003f/char0040", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0059/char005f", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0059/char005d", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0027/char0028", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0059/char005a", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0037", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", + { + { "org.bluez.Device1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez", + { + { "org.bluez.ProfileManager1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.bluez.AgentManager1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0037/char0038/desc003a", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0059/char005a/desc005c", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0061/char0062/desc0065", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0061/char0062/desc0064", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service001f", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0051/char0057", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0037/char0038", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0051/char0055", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service004c/char004f", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service004c/char004d", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0051/char0052", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service000c/char001d", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service003f/char0040/desc0042", + { + { "org.bluez.GattDescriptor1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0027", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0061", + { + { "org.bluez.GattService1" }, + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0047/char0048", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + }, + { + "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0061/char0066", + { + { "org.freedesktop.DBus.Introspectable" }, + { "org.freedesktop.DBus.Properties" }, + { "org.bluez.GattCharacteristic1" } + } + } +}; + +class FakeObjectManager +{ +public: + GList* get_objects() + { + auto length = sizeof(g_dbus_objects) / sizeof(g_dbus_objects[0]); + DBUSObject* bus_object = new DBUSObject(g_dbus_objects[0]); + CGList* head = new CGList((gpointer)(new RefCountObjectDelete(bus_object))); + for (size_t i = 1; i < length; i++) + { + bus_object = new DBUSObject(g_dbus_objects[i]); + head->append((gpointer)(new RefCountObjectDelete(bus_object))); + } + + return (GList*)head; + } +}; + +class AsyncCallFinisherBase +{ +public: + size_t call_count; + size_t when_shall_call_fail; + +public: + AsyncCallFinisherBase() : + call_count(0), + when_shall_call_fail(0) + {} + + void reset() + { + call_count = when_shall_call_fail = 0; + } +}; + +template +class AsyncCallFinisherValueType : public AsyncCallFinisherBase +{ +public: + T async_call_finish(GAsyncResult* res, GError** error) + { + ++call_count; + + T result; + if (when_shall_call_fail == call_count) + { + *error = g_error_new_literal(__LINE__, -1, "Whoops!"); + result = (T)0; + } + else + { + result = (T)1; + } + return result; + } +}; + +template +class AsyncCallFinisherRefType : public AsyncCallFinisherBase +{ +public: + T* async_call_finish(GAsyncResult* res, GError** error) + { + ++call_count; + T* result; + + if (when_shall_call_fail == call_count) + { + *error = g_error_new_literal(__LINE__, -1, "Whoops!"); + result = NULL; + } + else + { + result = (T*)(new RefCountObjectFree((gpointer)(uintptr_t)malloc(1))); + } + + return result; + } +}; + +static AsyncCallFinisherRefType g_bus_finisher; +static AsyncCallFinisherRefType g_bluez_device__proxy_new_finisher; +static AsyncCallFinisherValueType g_bluez_device__call_connect_finisher; +static AsyncCallFinisherValueType g_bluez_device__call_disconnect_finisher; +static AsyncCallFinisherRefType g_bluez_characteristic__proxy_new_finisher; +static AsyncCallFinisherValueType g_bluez_characteristic__call_read_value_finisher; +static AsyncCallFinisherValueType g_bluez_characteristic__call_write_value_finisher; + +// The BLE config we use in all tests. +static BLE_DEVICE_CONFIG g_device_config = +{ + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, 0 +}; + +static const char* g_bluez_device_path = "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF"; + +// We store the generated characteristic UUIDs in this vector so we can +// use them in tests. +std::vector g_char_uuids; + +TYPED_MOCK_CLASS(CBLEGATTIOMocks, CGlobalMock) +{ +public: + // memory + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, GTree*, g_tree_new_full, GCompareDataFunc, key_compare_func, gpointer, key_compare_data, GDestroyNotify, key_destroy_func, GDestroyNotify, value_destroy_func) + GTree* result2 = (GTree*)new CGTree(key_compare_func, key_compare_data, key_destroy_func, value_destroy_func); + MOCK_METHOD_END(GTree*, result2); + + MOCK_STATIC_METHOD_1(, void, g_tree_unref, GTree*, tree) + CGTree* gtree = (CGTree*)tree; + delete gtree; + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, void, g_tree_insert, GTree*, tree, gpointer, key, gpointer, value) + CGTree* gtree = (CGTree*)tree; + gtree->insert(key, value); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, gpointer, g_tree_lookup, GTree*, tree, gconstpointer, key) + CGTree* gtree = (CGTree*)tree; + gpointer result2 = const_cast(gtree->lookup((const gpointer)key)); + MOCK_METHOD_END(gpointer, result2); + + MOCK_STATIC_METHOD_1(, GString*, g_string_new, const gchar*, init) + GString* result2 = (GString*)malloc(sizeof(GString)); + result2->str = init != NULL ? g_strdup(init) : (gchar*)g_new0(gchar, 4); + result2->len = init != NULL ? strlen(init) : 0; + result2->allocated_len = init != NULL ? strlen(init) : 4; + MOCK_METHOD_END(GString*, result2); + + MOCK_STATIC_METHOD_2(, gchar*, g_string_free, GString*, string, gboolean, free_segment) + gchar* result2 = string->str; + if (free_segment == TRUE) + { + g_free(result2); + result2 = NULL; + } + free(string); + MOCK_METHOD_END(gchar*, result2); + + MOCK_STATIC_METHOD_2(, GBytes*, g_bytes_new, gconstpointer, data, gsize, size) + GBytes* result2 = (GBytes*)( + new RefCountObjectDelete( + new CGBytes(data, size) + ) + ); + MOCK_METHOD_END(GBytes*, result2); + + MOCK_STATIC_METHOD_2(, gconstpointer, g_bytes_get_data, GBytes*, bytes, gsize*, size) + CGBytes* cgbytes = (CGBytes*)((RefCountObjectDelete*)bytes)->object; + gconstpointer result2 = cgbytes->get_data(size); + MOCK_METHOD_END(gconstpointer, result2); + + MOCK_STATIC_METHOD_1(, void, g_bytes_unref, GBytes*, bytes) + ((RefCountObjectDelete*)bytes)->dec_ref(); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_5(, void, on_read_complete, BLEIO_GATT_HANDLE, bleio_gatt_handle, void*, context, BLEIO_GATT_RESULT, result2, const unsigned char*, buffer, size_t, size) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, void, on_write_complete, BLEIO_GATT_HANDLE, bleio_gatt_handle, void*, context, BLEIO_GATT_RESULT, result2) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, void, on_disconnect_complete, BLEIO_GATT_HANDLE, bleio_gatt_handle, void*, context) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, void, on_gatt_connect_complete, BLEIO_GATT_HANDLE, bleio_gatt_handle, void*, context, BLEIO_GATT_CONNECT_RESULT, connect_result) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, GIO_ASYNCSEQ_HANDLE, GIO_Async_Seq_Create, gpointer, async_seq_context, GIO_ASYNCSEQ_ERROR_CALLBACK, error_callback, GIO_ASYNCSEQ_COMPLETE_CALLBACK, complete_callback) + auto result2 = BASEIMPLEMENTATION::GIO_Async_Seq_Create(async_seq_context, error_callback, complete_callback); + MOCK_METHOD_END(GIO_ASYNCSEQ_HANDLE, result2); + + MOCK_STATIC_METHOD_1(, void, GIO_Async_Seq_Destroy, GIO_ASYNCSEQ_HANDLE, async_seq_handle) + BASEIMPLEMENTATION::GIO_Async_Seq_Destroy(async_seq_handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, gpointer, GIO_Async_Seq_GetContext, GIO_ASYNCSEQ_HANDLE, async_seq_handle) + auto result2 = BASEIMPLEMENTATION::GIO_Async_Seq_GetContext(async_seq_handle); + MOCK_METHOD_END(gpointer, result2); + + MOCK_STATIC_METHOD_1(, GIO_ASYNCSEQ_RESULT, GIO_Async_Seq_Run_Async, GIO_ASYNCSEQ_HANDLE, async_seq_handle) + auto result2 = BASEIMPLEMENTATION::GIO_Async_Seq_Run_Async(async_seq_handle); + MOCK_METHOD_END(GIO_ASYNCSEQ_RESULT, result2); + + MOCK_STATIC_METHOD_1(, void, g_object_unref, gpointer, object) + RefCountObjectBase* refobj = (RefCountObjectBase*)object; + refobj->dec_ref(); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_4(, void, g_bus_get, GBusType, bus_type, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data) + callback(NULL, NULL, user_data); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_2(, GDBusConnection*, g_bus_get_finish, GAsyncResult*, res, GError**, error) + GDBusConnection* result2 = g_bus_finisher.async_call_finish(res, error); + MOCK_METHOD_END(GDBusConnection*, result2); + + MOCK_STATIC_METHOD_10(, void, g_dbus_object_manager_client_new, GDBusConnection*, connection, GDBusObjectManagerClientFlags, flags, const gchar*, name, const gchar*, object_path, GDBusProxyTypeFunc, get_proxy_type_func, gpointer, get_proxy_type_user_data, GDestroyNotify, get_proxy_type_destroy_notify, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data) + callback(NULL, NULL, user_data); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_2(, GDBusObjectManager*, g_dbus_object_manager_client_new_finish, GAsyncResult*, res, GError**, error) + GDBusObjectManager* result2 = (GDBusObjectManager*)new RefCountObjectDelete( + new FakeObjectManager() + ); + MOCK_METHOD_END(GDBusObjectManager*, result2); + + MOCK_STATIC_METHOD_7(, void, bluez_device__proxy_new, GDBusConnection*, connection, GDBusProxyFlags, flags, const gchar*, name, const gchar*, object_path, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data) + callback(NULL, NULL, user_data); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_2(, bluezdevice*, bluez_device__proxy_new_finish, GAsyncResult*, res, GError**, error) + bluezdevice* result2 = g_bluez_device__proxy_new_finisher.async_call_finish(res, error); + MOCK_METHOD_END(bluezdevice*, result2); + + MOCK_STATIC_METHOD_4(, void, bluez_device__call_connect, bluezdevice*, proxy, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data) + callback(NULL, NULL, user_data); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_3(, gboolean, bluez_device__call_connect_finish, bluezdevice*, proxy, GAsyncResult*, res, GError**, error) + gboolean result2 = g_bluez_device__call_connect_finisher.async_call_finish(res, error); + MOCK_METHOD_END(gboolean, result2); + + MOCK_STATIC_METHOD_4(, void, bluez_device__call_disconnect, bluezdevice*, proxy, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data) + callback(NULL, NULL, user_data); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_3(, gboolean, bluez_device__call_disconnect_finish, bluezdevice*, proxy, GAsyncResult*, res, GError**, error) + gboolean result2 = g_bluez_device__call_disconnect_finisher.async_call_finish(res, error); + MOCK_METHOD_END(gboolean, result2); + + MOCK_STATIC_METHOD_7(, void, bluez_characteristic__proxy_new, GDBusConnection*, connection, GDBusProxyFlags, flags, const gchar*, name, const gchar*, object_path, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data) + callback(NULL, NULL, user_data); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_2(, bluezcharacteristic*, bluez_characteristic__proxy_new_finish, GAsyncResult*, res, GError**, error) + bluezcharacteristic* result2 = g_bluez_characteristic__proxy_new_finisher.async_call_finish(res, error); + MOCK_METHOD_END(bluezcharacteristic*, result2); + + MOCK_STATIC_METHOD_4(, void, bluez_characteristic__call_read_value, bluezcharacteristic*, proxy, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data) + callback(NULL, NULL, user_data); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_4(, gboolean, bluez_characteristic__call_read_value_finish, bluezcharacteristic*, proxy, gchar**, out_value, GAsyncResult*, res, GError**, error) + gboolean result2 = g_bluez_characteristic__call_read_value_finisher.async_call_finish(res, error); + MOCK_METHOD_END(gboolean, result2); + + MOCK_STATIC_METHOD_5(, void, bluez_characteristic__call_write_value, bluezcharacteristic*, proxy, const gchar *, arg_value, GCancellable *, cancellable, GAsyncReadyCallback, callback, gpointer, user_data) + callback(NULL, NULL, user_data); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_3(, gboolean, bluez_characteristic__call_write_value_finish, bluezcharacteristic*, proxy, GAsyncResult*, res, GError**, error) + gboolean result2 = g_bluez_characteristic__call_write_value_finisher.async_call_finish(res, error); + MOCK_METHOD_END(gboolean, result2); + + MOCK_STATIC_METHOD_1(, GList*, g_dbus_object_manager_get_objects, GDBusObjectManager*, manager) + auto om = (FakeObjectManager*)((RefCountObjectDelete*)manager)->object; + GList* result2 = om->get_objects(); + MOCK_METHOD_END(GList*, result2); + + MOCK_STATIC_METHOD_3(, GList*, g_list_find_custom, GList*, list, gconstpointer, data, GCompareFunc, func) + GList* result2 = ((CGList*)list)->find_custom(data, func); + MOCK_METHOD_END(GList*, result2); + + MOCK_STATIC_METHOD_3(, void, g_list_foreach, GList*, list, GFunc, func, gpointer, user_data) + ((CGList*)list)->foreach(func, user_data); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_1(, void, g_list_free, GList*, list) + delete ((CGList*)list); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_1(, const gchar*, g_dbus_proxy_get_object_path, GDBusProxy*, proxy) + // this is called only for the device; so we return the device path + const gchar* result2 = g_bluez_device_path; + MOCK_METHOD_END(const gchar*, result2); + + MOCK_STATIC_METHOD_1(, const gchar*, g_dbus_proxy_get_interface_name, GDBusProxy*, proxy) + DBUSInterface* dbus_interface = (DBUSInterface*)((RefCountObjectDelete*)proxy)->object; + const gchar* result2 = dbus_interface->name.c_str(); + MOCK_METHOD_END(const gchar*, result2); + + MOCK_STATIC_METHOD_2(, GVariant*, g_dbus_proxy_get_cached_property, GDBusProxy*, proxy, const gchar*, property_name) + // this is called only to get the UUID of a characteristic; we return a fake one + char uuid_str[40]; + generate_fake_uuid(uuid_str); + g_char_uuids.push_back(uuid_str); + GVariant* result2 = g_variant_new_string(uuid_str); + MOCK_METHOD_END(GVariant*, result2); + + MOCK_STATIC_METHOD_8(, void, g_dbus_proxy_call, GDBusProxy*, proxy, const gchar*, method_name, GVariant*, parameters, GDBusCallFlags, flags, gint, timeout_msec, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data) + g_variant_unref(parameters); + callback(NULL, NULL, user_data); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_3(, GVariant*, g_dbus_proxy_call_finish, GDBusProxy*, proxy, GAsyncResult*, res, GError**, error) + *error = NULL; + uint8_t fake_data[] = { 0xde, 0xad, 0xbe, 0xef }; + size_t size_data = sizeof(fake_data) / sizeof(uint8_t); + GBytes* data = g_bytes_new(fake_data, size_data); + GVariant* result2 = g_variant_new_from_bytes( + G_VARIANT_TYPE_BYTESTRING, + data, + TRUE + ); + MOCK_METHOD_END(GVariant*, result2); + + MOCK_STATIC_METHOD_1(, GVariant*, g_variant_new_string, const gchar*, string) + GVariant* result2 = (GVariant*)(new RefCountObjectDelete( + new CGVariant(string) + ) + ); + MOCK_METHOD_END(GVariant*, result2); + + MOCK_STATIC_METHOD_2(, const gchar*, g_variant_get_string, GVariant*, value, gsize*, length) + CGVariant* var = ((RefCountObjectDelete*)value)->object; + const gchar* result2 = var->store.data.str->str; + if (length != NULL) + { + *length = var->store.data.str->len; + } + MOCK_METHOD_END(const gchar*, result2); + + MOCK_STATIC_METHOD_3(, GVariant*, g_variant_new_from_bytes, const GVariantType*, type, GBytes*, bytes, gboolean, trusted) + GVariant* result2 = (GVariant*)(new RefCountObjectDelete( + new CGVariant((RefCountObjectDelete*)bytes) + ) + ); + MOCK_METHOD_END(GVariant*, result2); + + MOCK_STATIC_METHOD_1(, GBytes*, g_variant_get_data_as_bytes, GVariant*, value) + CGVariant* var = ((RefCountObjectDelete*)value)->object; + GBytes* result2 = (GBytes*)var->store.data.bytes; + MOCK_METHOD_END(GBytes*, result2); + + MOCK_STATIC_METHOD_4(, GVariant*, g_variant_new_fixed_array, const GVariantType*, element_type, gconstpointer, elements, gsize, n_elements, gsize, element_size) + GVariant* result2 = (GVariant*)(new RefCountObjectDelete( + new CGVariant(new CGBytes(elements, n_elements * element_size)) + ) + ); + MOCK_METHOD_END(GVariant*, result2); + + MOCK_STATIC_METHOD_2(, GVariant*, g_variant_new_tuple, GVariant * const *, children, gsize, n_children) + GVariant* result2 = *children; + MOCK_METHOD_END(GVariant*, result2); + + MOCK_STATIC_METHOD_1(, void, g_variant_unref, GVariant*, value) + ((RefCountObjectDelete*)value)->dec_ref(); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_1(, const gchar*, g_dbus_object_get_object_path, GDBusObject*, object) + DBUSObject* dbus_object = (DBUSObject*)((RefCountObjectDelete*)object)->object; + const gchar* result2 = dbus_object->object_path.c_str(); + MOCK_METHOD_END(const gchar*, result2); + + MOCK_STATIC_METHOD_1(, GList*, g_dbus_object_get_interfaces, GDBusObject*, object) + DBUSObject* dbus_object = (DBUSObject*)((RefCountObjectDelete*)object)->object; + const auto& interfaces_supported = dbus_object->interfaces_supported; + CGList* result2; + if (interfaces_supported.empty()) + { + result2 = NULL; + } + else + { + DBUSInterface* interface = new DBUSInterface(interfaces_supported[0]); + result2 = new CGList((gpointer)(new RefCountObjectDelete(interface))); + for (size_t i = 1; i < interfaces_supported.size(); i++) + { + interface = new DBUSInterface(interfaces_supported[i]); + result2->append((gpointer)(new RefCountObjectDelete(interface))); + } + } + MOCK_METHOD_END(GList*, (GList*)result2); +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , void, gballoc_free, void*, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEGATTIOMocks, , GTree*, g_tree_new_full, GCompareDataFunc, key_compare_func, gpointer, key_compare_data, GDestroyNotify, key_destroy_func, GDestroyNotify, value_destroy_func); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , void, g_tree_unref, GTree*, tree); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEGATTIOMocks, , void, g_tree_insert, GTree*, tree, gpointer, key, gpointer, value); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , gpointer, g_tree_lookup, GTree*, tree, gconstpointer, key); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEGATTIOMocks, , GList*, g_list_find_custom, GList*, list, gconstpointer, data, GCompareFunc, func); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEGATTIOMocks, , void, g_list_foreach, GList*, list, GFunc, func, gpointer, user_data); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , void, g_list_free, GList*, list); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , GString*, g_string_new, const gchar*, init) +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , gchar*, g_string_free, GString*, string, gboolean, free_segment) + +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , GBytes*, g_bytes_new, gconstpointer, data, gsize, size); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , gconstpointer, g_bytes_get_data, GBytes*, bytes, gsize*, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , void, g_bytes_unref, GBytes*, bytes); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , GBytes*, g_variant_get_data_as_bytes, GVariant*, value); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , void, g_variant_unref, GVariant*, value); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , GVariant*, g_variant_new_string, const gchar*, string); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , const gchar*, g_variant_get_string, GVariant*, value, gsize*, length); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEGATTIOMocks, , GVariant*, g_variant_new_from_bytes, const GVariantType*, type, GBytes*, bytes, gboolean, trusted); +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEGATTIOMocks, , GVariant*, g_variant_new_fixed_array, const GVariantType*, element_type, gconstpointer, elements, gsize, n_elements, gsize, element_size); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , GVariant*, g_variant_new_tuple, GVariant * const *, children, gsize, n_children); + +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEGATTIOMocks, , GIO_ASYNCSEQ_HANDLE, GIO_Async_Seq_Create, gpointer, async_seq_context, GIO_ASYNCSEQ_ERROR_CALLBACK, error_callback, GIO_ASYNCSEQ_COMPLETE_CALLBACK, complete_callback); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , void, GIO_Async_Seq_Destroy, GIO_ASYNCSEQ_HANDLE, async_seq_handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , gpointer, GIO_Async_Seq_GetContext, GIO_ASYNCSEQ_HANDLE, async_seq_handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , GIO_ASYNCSEQ_RESULT, GIO_Async_Seq_Run_Async, GIO_ASYNCSEQ_HANDLE, async_seq_handle); + +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEGATTIOMocks, , void, g_bus_get, GBusType, bus_type, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , GDBusConnection*, g_bus_get_finish, GAsyncResult*, res, GError**, error); + +DECLARE_GLOBAL_MOCK_METHOD_10(CBLEGATTIOMocks, , void, g_dbus_object_manager_client_new, GDBusConnection*, connection, GDBusObjectManagerClientFlags, flags, const gchar*, name, const gchar*, object_path, GDBusProxyTypeFunc, get_proxy_type_func, gpointer, get_proxy_type_user_data, GDestroyNotify, get_proxy_type_destroy_notify, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , GDBusObjectManager*, g_dbus_object_manager_client_new_finish, GAsyncResult*, res, GError**, error); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , GList*, g_dbus_object_manager_get_objects, GDBusObjectManager*, manager); + +DECLARE_GLOBAL_MOCK_METHOD_7(CBLEGATTIOMocks, , void, bluez_device__proxy_new, GDBusConnection*, connection, GDBusProxyFlags, flags, const gchar*, name, const gchar*, object_path, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , bluezdevice*, bluez_device__proxy_new_finish, GAsyncResult*, res, GError**, error); + +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEGATTIOMocks, , void, bluez_device__call_connect, bluezdevice*, proxy, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEGATTIOMocks, , gboolean, bluez_device__call_connect_finish, bluezdevice*, proxy, GAsyncResult*, res, GError**, error); + +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEGATTIOMocks, , void, bluez_device__call_disconnect, bluezdevice*, proxy, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEGATTIOMocks, , gboolean, bluez_device__call_disconnect_finish, bluezdevice*, proxy, GAsyncResult*, res, GError**, error); + +DECLARE_GLOBAL_MOCK_METHOD_7(CBLEGATTIOMocks, , void, bluez_characteristic__proxy_new, GDBusConnection*, connection, GDBusProxyFlags, flags, const gchar*, name, const gchar*, object_path, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , bluezcharacteristic*, bluez_characteristic__proxy_new_finish, GAsyncResult*, res, GError**, error); + +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEGATTIOMocks, , void, bluez_characteristic__call_read_value, bluezcharacteristic*, proxy, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data); +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEGATTIOMocks, , gboolean, bluez_characteristic__call_read_value_finish, bluezcharacteristic*, proxy, gchar**, out_value, GAsyncResult*, res, GError**, error); + +DECLARE_GLOBAL_MOCK_METHOD_5(CBLEGATTIOMocks, , void, bluez_characteristic__call_write_value, bluezcharacteristic*, proxy, const gchar *, arg_value, GCancellable *, cancellable, GAsyncReadyCallback, callback, gpointer, user_data); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEGATTIOMocks, , gboolean, bluez_characteristic__call_write_value_finish, bluezcharacteristic*, proxy, GAsyncResult*, res, GError**, error); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , const gchar*, g_dbus_proxy_get_object_path, GDBusProxy*, proxy); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , const gchar*, g_dbus_proxy_get_interface_name, GDBusProxy*, proxy); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , const gchar*, g_dbus_object_get_object_path, GDBusObject*, object); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , GList*, g_dbus_object_get_interfaces, GDBusObject*, object); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , GVariant*, g_dbus_proxy_get_cached_property, GDBusProxy*, proxy, const gchar*, property_name); +DECLARE_GLOBAL_MOCK_METHOD_8(CBLEGATTIOMocks, , void, g_dbus_proxy_call, GDBusProxy*, proxy, const gchar*, method_name, GVariant*, parameters, GDBusCallFlags, flags, gint, timeout_msec, GCancellable*, cancellable, GAsyncReadyCallback, callback, gpointer, user_data) +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEGATTIOMocks, , GVariant*, g_dbus_proxy_call_finish, GDBusProxy*, proxy, GAsyncResult*, res, GError**, error); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , void, g_object_unref, gpointer, object); + +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEGATTIOMocks, , void, on_gatt_connect_complete, BLEIO_GATT_HANDLE, bleio_gatt_handle, void*, context, BLEIO_GATT_CONNECT_RESULT, connect_result); +DECLARE_GLOBAL_MOCK_METHOD_5(CBLEGATTIOMocks, , void, on_read_complete, BLEIO_GATT_HANDLE, bleio_gatt_handle, void*, context, BLEIO_GATT_RESULT, result2, const unsigned char*, buffer, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEGATTIOMocks, , void, on_write_complete, BLEIO_GATT_HANDLE, bleio_gatt_handle, void*, context, BLEIO_GATT_RESULT, result2); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEGATTIOMocks, , void, on_disconnect_complete, BLEIO_GATT_HANDLE, bleio_gatt_handle, void*, context); + +/** + * Poor man's mock for the var args function GIO_Async_Seq_Add. + */ +static bool g_was_GIO_Async_Seq_Add_called = false; +static size_t g_when_shall_GIO_Async_Seq_Add_fail = 0; +static size_t g_GIO_Async_Seq_Add_call = 0; +GIO_ASYNCSEQ_RESULT GIO_Async_Seq_Add( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer callback_context, + ... +) +{ + ++g_GIO_Async_Seq_Add_call; + g_was_GIO_Async_Seq_Add_called = true; + + va_list args; + va_start(args, callback_context); + GIO_ASYNCSEQ_RESULT result; + if (g_GIO_Async_Seq_Add_call == g_when_shall_GIO_Async_Seq_Add_fail) + { + result = GIO_ASYNCSEQ_ERROR; + } + else + { + result = BASEIMPLEMENTATION::GIO_Async_Seq_Addv(async_seq_handle, callback_context, args); + } + va_end(args); + return result; +} + +BEGIN_TEST_SUITE(ble_gatt_io_unittests) + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + + g_was_GIO_Async_Seq_Add_called = false; + g_when_shall_GIO_Async_Seq_Add_fail = 0; + g_GIO_Async_Seq_Add_call = 0; + + g_bus_finisher.reset(); + g_bluez_device__proxy_new_finisher.reset(); + g_bluez_device__call_connect_finisher.reset(); + g_bluez_device__call_disconnect_finisher.reset(); + g_bluez_characteristic__proxy_new_finisher.reset(); + g_bluez_characteristic__call_read_value_finisher.reset(); + g_bluez_characteristic__call_write_value_finisher.reset(); + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + + g_char_uuids.clear(); + } + + /*Tests_SRS_BLEIO_GATT_13_003: [ BLEIO_gatt_create shall return NULL if config is NULL. ]*/ + TEST_FUNCTION(BLEIO_gatt_create_returns_NULL_for_NULL_input) + { + ///arrange + CBLEGATTIOMocks mocks; + + ///act + auto result = BLEIO_gatt_create(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_002: [ BLEIO_gatt_create shall return NULL when any of the underlying platform calls fail. ]*/ + TEST_FUNCTION(BLEIO_gatt_create_returns_NULL_when_malloc_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + auto result = BLEIO_gatt_create(&g_device_config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_002: [ BLEIO_gatt_create shall return NULL when any of the underlying platform calls fail. ]*/ + TEST_FUNCTION(BLEIO_gatt_create_returns_NULL_when_g_tree_new_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_new_full(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetFailReturn((GTree*)NULL); + + ///act + auto result = BLEIO_gatt_create(&g_device_config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_001: [ BLEIO_gatt_create shall return a non-NULL handle on successful execution. ]*/ + TEST_FUNCTION(BLEIO_gatt_create_succeeds) + { + ///arrange + CBLEGATTIOMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_new_full(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + auto result = BLEIO_gatt_create(&g_device_config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + BLEIO_gatt_destroy(result); + } + + /*Tests_SRS_BLEIO_GATT_13_005: [ If bleio_gatt_handle is NULL BLEIO_gatt_destroy shall do nothing. ]*/ + TEST_FUNCTION(BLEIO_gatt_destroy_does_nothing_if_handle_is_NULL) + { + ///arrange + CBLEGATTIOMocks mocks; + + ///act + BLEIO_gatt_destroy(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_004: [ BLEIO_gatt_destroy shall free all resources associated with the handle. ]*/ + TEST_FUNCTION(BLEIO_gatt_destroy_frees_things_with_disconnected_handle) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_tree_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLEIO_gatt_destroy(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_004: [ BLEIO_gatt_destroy shall free all resources associated with the handle. ]*/ + TEST_FUNCTION(BLEIO_gatt_destroy_frees_things_with_connected_handle) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // bus + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // object manager + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // device + STRICT_EXPECTED_CALL(mocks, g_tree_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + // this is the number of g_string_free calls we expect + const size_t EXPECTED_STRING_FREES = ( + (sizeof(g_dbus_objects) / sizeof(g_dbus_objects[0])) + 5 + ); + for (size_t i = 0; i < EXPECTED_STRING_FREES; i++) + { + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + } + + ///act + BLEIO_gatt_destroy(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_010: [ If bleio_gatt_handle or on_bleio_gatt_connect_complete are NULL then BLEIO_gatt_connect shall return a non-zero value. ]*/ + TEST_FUNCTION(BLEIO_gatt_connect_returns_non_zero_with_NULL_handle) + { + ///arrange + CBLEGATTIOMocks mocks; + + ///act + auto result = BLEIO_gatt_connect(NULL, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_010: [ If bleio_gatt_handle or on_bleio_gatt_connect_complete are NULL then BLEIO_gatt_connect shall return a non-zero value. ]*/ + TEST_FUNCTION(BLEIO_gatt_connect_returns_non_zero_with_NULL_callback) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + ///act + auto result = BLEIO_gatt_connect(handle, NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_047: [ If when BLEIO_gatt_connect is called, there's another connection request already in progress or if an open connection already exists, then this API shall return a non-zero error code. ]*/ + TEST_FUNCTION(BLEIO_gatt_connect_returns_non_zero_when_in_invalid_state) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + mocks.ResetAllCalls(); + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_009: [ If any of the underlying platform calls fail, BLEIO_gatt_connect shall return a non-zero value. ]*/ + TEST_FUNCTION(BLEIO_gatt_connect_returns_non_zero_when_malloc_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_009: [ If any of the underlying platform calls fail, BLEIO_gatt_connect shall return a non-zero value. ]*/ + TEST_FUNCTION(BLEIO_gatt_connect_returns_non_zero_when_GIO_Async_Seq_Create_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetFailReturn((GIO_ASYNCSEQ_HANDLE)NULL); + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_009: [ If any of the underlying platform calls fail, BLEIO_gatt_connect shall return a non-zero value. ]*/ + TEST_FUNCTION(BLEIO_gatt_connect_returns_non_zero_when_GIO_Async_Seq_Add_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + // since GIO_Async_Seq_Add has not been mocked because it's a varargs + // function, we use this variable to control it's execution + g_when_shall_GIO_Async_Seq_Add_fail = 1; + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_009: [ If any of the underlying platform calls fail, BLEIO_gatt_connect shall return a non-zero value. ]*/ + TEST_FUNCTION(BLEIO_gatt_connect_returns_non_zero_when_GIO_Async_Seq_Run_Async_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((GIO_ASYNCSEQ_RESULT)GIO_ASYNCSEQ_ERROR); + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_013: [The connect_result parameter of the on_bleio_gatt_connect_complete callback shall indicate the status of the connect operation.]*/ + TEST_FUNCTION(BLEIO_gatt_connect_calls_callback_with_error_when_g_bus_get_finish_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bus_get(G_BUS_TYPE_SYSTEM, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + g_bus_finisher.when_shall_call_fail = 1; + STRICT_EXPECTED_CALL(mocks, g_bus_get_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, on_gatt_connect_complete(handle, NULL, BLEIO_GATT_CONNECT_ERROR)); + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result == 0); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_013: [The connect_result parameter of the on_bleio_gatt_connect_complete callback shall indicate the status of the connect operation.]*/ + TEST_FUNCTION(BLEIO_gatt_connect_calls_callback_with_error_when_g_dbus_object_manager_client_new_finish_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bus_get(G_BUS_TYPE_SYSTEM, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, g_bus_get_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new( + IGNORED_PTR_ARG, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + IGNORED_PTR_ARG, + IGNORED_PTR_ARG, + NULL, NULL, NULL, NULL, + IGNORED_PTR_ARG, + IGNORED_PTR_ARG + )) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(9) + .IgnoreArgument(10); + + GError* expected_error = g_error_new_literal(__LINE__, -1, "Whoops!"); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn((GDBusObjectManager*)NULL) + .CopyOutArgumentBuffer(2, &expected_error, sizeof(GError*)); + STRICT_EXPECTED_CALL(mocks, on_gatt_connect_complete(handle, NULL, BLEIO_GATT_CONNECT_ERROR)); + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result == 0); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_013: [The connect_result parameter of the on_bleio_gatt_connect_complete callback shall indicate the status of the connect operation.]*/ + TEST_FUNCTION(BLEIO_gatt_connect_calls_callback_with_error_when_bluez_device__proxy_new_finish_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bus_get(G_BUS_TYPE_SYSTEM, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, g_bus_get_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new( + IGNORED_PTR_ARG, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + IGNORED_PTR_ARG, + IGNORED_PTR_ARG, + NULL, NULL, NULL, NULL, + IGNORED_PTR_ARG, + IGNORED_PTR_ARG + )) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(9) + .IgnoreArgument(10); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, bluez_device__proxy_new( + IGNORED_PTR_ARG, + G_DBUS_PROXY_FLAGS_NONE, + IGNORED_PTR_ARG, + IGNORED_PTR_ARG, + NULL, + IGNORED_PTR_ARG, + IGNORED_PTR_ARG + )) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + g_bluez_device__proxy_new_finisher.when_shall_call_fail = 1; + STRICT_EXPECTED_CALL(mocks, bluez_device__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, on_gatt_connect_complete(handle, NULL, BLEIO_GATT_CONNECT_ERROR)); + STRICT_EXPECTED_CALL(mocks, g_string_new(NULL)); // create_device_proxy + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result == 0); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_013: [The connect_result parameter of the on_bleio_gatt_connect_complete callback shall indicate the status of the connect operation.]*/ + TEST_FUNCTION(BLEIO_gatt_connect_calls_callback_with_error_when_bluez_device__call_connect_finish_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bus_get(G_BUS_TYPE_SYSTEM, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, g_bus_get_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new( + IGNORED_PTR_ARG, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + IGNORED_PTR_ARG, + IGNORED_PTR_ARG, + NULL, NULL, NULL, NULL, + IGNORED_PTR_ARG, + IGNORED_PTR_ARG + )) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(9) + .IgnoreArgument(10); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, bluez_device__proxy_new( + IGNORED_PTR_ARG, + G_DBUS_PROXY_FLAGS_NONE, + IGNORED_PTR_ARG, + IGNORED_PTR_ARG, + NULL, + IGNORED_PTR_ARG, + IGNORED_PTR_ARG + )) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + STRICT_EXPECTED_CALL(mocks, bluez_device__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, bluez_device__call_connect( + IGNORED_PTR_ARG, + NULL, + IGNORED_PTR_ARG, + IGNORED_PTR_ARG + )) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4); + g_bluez_device__call_connect_finisher.when_shall_call_fail = 1; + STRICT_EXPECTED_CALL(mocks, bluez_device__call_connect_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, on_gatt_connect_complete(handle, NULL, BLEIO_GATT_CONNECT_ERROR)); + STRICT_EXPECTED_CALL(mocks, g_string_new(NULL)); // create_device_proxy + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result == 0); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_013: [The connect_result parameter of the on_bleio_gatt_connect_complete callback shall indicate the status of the connect operation.]*/ + TEST_FUNCTION(BLEIO_gatt_connect_calls_callback_with_error_when_g_dbus_object_manager_get_objects_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bus_get(G_BUS_TYPE_SYSTEM, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, g_bus_get_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new(IGNORED_PTR_ARG, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, NULL, NULL, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(9) + .IgnoreArgument(10); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_get_objects(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((GList*)NULL); + STRICT_EXPECTED_CALL(mocks, bluez_device__proxy_new(IGNORED_PTR_ARG, G_DBUS_PROXY_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + STRICT_EXPECTED_CALL(mocks, bluez_device__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, bluez_device__call_connect(IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, bluez_device__call_connect_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, on_gatt_connect_complete(handle, NULL, BLEIO_GATT_CONNECT_ERROR)); + STRICT_EXPECTED_CALL(mocks, g_string_new(NULL)); // create_device_proxy + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result == 0); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_013: [The connect_result parameter of the on_bleio_gatt_connect_complete callback shall indicate the status of the connect operation.]*/ + /*Tests_SRS_BLEIO_GATT_13_007: [ BLEIO_gatt_connect shall asynchronously attempt to open a connection with the BLE device. ]*/ + /*Tests_SRS_BLEIO_GATT_13_008: [ On initiating the connect successfully, BLEIO_gatt_connect shall return 0 (zero). ]*/ + /*Tests_SRS_BLEIO_GATT_13_011: [ When the connect operation to the device has been completed, the callback function pointed at by on_bleio_gatt_connect_complete shall be invoked. ]*/ + TEST_FUNCTION(BLEIO_gatt_connect_succeeds) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bus_get(G_BUS_TYPE_SYSTEM, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, g_bus_get_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new(IGNORED_PTR_ARG, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, NULL, NULL, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(9) + .IgnoreArgument(10); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_get_objects(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, bluez_device__proxy_new(IGNORED_PTR_ARG, G_DBUS_PROXY_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + STRICT_EXPECTED_CALL(mocks, bluez_device__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, bluez_device__call_connect(IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, bluez_device__call_connect_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, g_list_foreach(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, g_list_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_new(NULL)); // create_device_proxy + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + + auto dbus_objects_length = sizeof(g_dbus_objects) / sizeof(g_dbus_objects[0]); + std::string device_path = g_bluez_device_path; + std::string characteristic_interface_name = "org.bluez.GattCharacteristic1"; + + for (size_t i = 0; i < dbus_objects_length; i++) + { + // these APIs should get called once for every object on the bus + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_get_object_path(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_get_object_path(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + // if device_path is a prefix of the object_path then the + // object is a child of the device + if (device_path != g_dbus_objects[i].object_path && + std::equal( + device_path.begin(), + device_path.end(), + g_dbus_objects[i].object_path.begin()) == true) + { + // these APIs should get called once for all objects that are a child of + // the device object + STRICT_EXPECTED_CALL(mocks, g_dbus_object_get_interfaces(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, g_list_find_custom(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + + // g_dbus_proxy_get_interface_name should get called for each interface on the + // object till "org.bluez.GattCharacteristic1" is encountered + const auto& interfaces_supported = g_dbus_objects[i].interfaces_supported; + for ( + std::vector::const_iterator it =interfaces_supported.begin(); + it != interfaces_supported.end(); + ++it + ) + { + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_get_interface_name(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + if (it->name == characteristic_interface_name) + { + // if the interface "org.bluez.GattCharacteristic1" is found then the following + // APIs should get called + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // process_object + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // get_characteristic_uuid + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // inside mock implementation of g_variant_new_string + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); // inside mock implementation of g_variant_unref + STRICT_EXPECTED_CALL(mocks, g_variant_get_string(IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1); // get_characteristic_uuid + STRICT_EXPECTED_CALL(mocks, g_variant_new_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // inside mock implementation of g_dbus_proxy_get_cached_property + STRICT_EXPECTED_CALL(mocks, g_variant_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // get_characteristic_uuid + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_get_cached_property(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_tree_insert(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + break; + } + } + + STRICT_EXPECTED_CALL(mocks, g_list_foreach(IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_list_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + } + } + STRICT_EXPECTED_CALL(mocks, on_gatt_connect_complete(handle, NULL, BLEIO_GATT_CONNECT_OK)); + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result == 0); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_013: [The connect_result parameter of the on_bleio_gatt_connect_complete callback shall indicate the status of the connect operation.]*/ + /*Tests_SRS_BLEIO_GATT_13_007: [ BLEIO_gatt_connect shall asynchronously attempt to open a connection with the BLE device. ]*/ + /*Tests_SRS_BLEIO_GATT_13_008: [ On initiating the connect successfully, BLEIO_gatt_connect shall return 0 (zero). ]*/ + /*Tests_SRS_BLEIO_GATT_13_011: [ When the connect operation to the device has been completed, the callback function pointed at by on_bleio_gatt_connect_complete shall be invoked. ]*/ + /*Tests_SRS_BLEIO_GATT_13_012: [ When on_bleio_gatt_connect_complete is invoked the value passed in callback_context to BLEIO_gatt_connect shall be passed along to on_bleio_gatt_connect_complete. ]*/ + TEST_FUNCTION(BLEIO_gatt_connect_passes_context) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bus_get(G_BUS_TYPE_SYSTEM, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, g_bus_get_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new(IGNORED_PTR_ARG, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, NULL, NULL, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(9) + .IgnoreArgument(10); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_client_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_manager_get_objects(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, bluez_device__proxy_new(IGNORED_PTR_ARG, G_DBUS_PROXY_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + STRICT_EXPECTED_CALL(mocks, bluez_device__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, bluez_device__call_connect(IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, bluez_device__call_connect_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, g_list_foreach(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, g_list_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_new(NULL)); // create_device_proxy + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + + auto dbus_objects_length = sizeof(g_dbus_objects) / sizeof(g_dbus_objects[0]); + std::string device_path = g_bluez_device_path; + std::string characteristic_interface_name = "org.bluez.GattCharacteristic1"; + + for (size_t i = 0; i < dbus_objects_length; i++) + { + // these APIs should get called once for every object on the bus + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_get_object_path(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_dbus_object_get_object_path(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + // if device_path is a prefix of the object_path then the + // object is a child of the device + if (device_path != g_dbus_objects[i].object_path && + std::equal( + device_path.begin(), + device_path.end(), + g_dbus_objects[i].object_path.begin()) == true) + { + // these APIs should get called once for all objects that are a child of + // the device object + STRICT_EXPECTED_CALL(mocks, g_dbus_object_get_interfaces(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, g_list_find_custom(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + + // g_dbus_proxy_get_interface_name should get called for each interface on the + // object till "org.bluez.GattCharacteristic1" is encountered + const auto& interfaces_supported = g_dbus_objects[i].interfaces_supported; + for ( + std::vector::const_iterator it =interfaces_supported.begin(); + it != interfaces_supported.end(); + ++it + ) + { + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_get_interface_name(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + if (it->name == characteristic_interface_name) + { + // if the interface "org.bluez.GattCharacteristic1" is found then the following + // APIs should get called + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // process_object + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // get_characteristic_uuid + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // inside mock implementation of g_variant_new_string + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); // inside mock implementation of g_variant_unref + STRICT_EXPECTED_CALL(mocks, g_variant_get_string(IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1); // get_characteristic_uuid + STRICT_EXPECTED_CALL(mocks, g_variant_new_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // inside mock implementation of g_dbus_proxy_get_cached_property + STRICT_EXPECTED_CALL(mocks, g_variant_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // get_characteristic_uuid + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_get_cached_property(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_tree_insert(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + break; + } + } + + STRICT_EXPECTED_CALL(mocks, g_list_foreach(IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_list_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + } + } + STRICT_EXPECTED_CALL(mocks, on_gatt_connect_complete(handle, (void*)0x42, BLEIO_GATT_CONNECT_OK)); + + ///act + auto result = BLEIO_gatt_connect(handle, on_gatt_connect_complete, (void*)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result == 0); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_027: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if bleio_gatt_handle is NULL. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_returns_non_zero_for_NULL_input1) + { + ///arrange + CBLEGATTIOMocks mocks; + + ///act + auto result = BLEIO_gatt_read_char_by_uuid(NULL, "fake_uuid", on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_051: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if ble_uuid is NULL. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_returns_non_zero_for_NULL_input2) + { + ///arrange + CBLEGATTIOMocks mocks; + + ///act + auto result = BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, NULL, on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_028: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if on_bleio_gatt_attrib_read_complete is NULL. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_returns_non_zero_for_NULL_input3) + { + ///arrange + CBLEGATTIOMocks mocks; + + ///act + auto result = BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, "fake_uuid", NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_052: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if the object is not in a connected state. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_returns_non_zero_when_not_connected) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + ///act + auto result = BLEIO_gatt_read_char_by_uuid(handle, "fake_uuid", on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_returns_non_zero_when_g_string_new_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new("fake_uuid")) + .SetFailReturn((GString*)NULL); + + ///act + auto result = BLEIO_gatt_read_char_by_uuid(handle, "fake_uuid", on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_returns_non_zero_when_char_uuid_lookup_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new("fake_uuid")); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + + ///act + auto result = BLEIO_gatt_read_char_by_uuid(handle, "fake_uuid", on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_returns_non_zero_when_malloc_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(g_char_uuids[0].c_str())); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + auto result = BLEIO_gatt_read_char_by_uuid(handle, g_char_uuids[0].c_str(), on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_returns_non_zero_when_GIO_Async_Seq_Create_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + auto char_uuid = g_char_uuids[0].c_str(); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetFailReturn((GIO_ASYNCSEQ_HANDLE)NULL); + + ///act + auto result = BLEIO_gatt_read_char_by_uuid(handle, char_uuid, on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_returns_non_zero_when_GIO_Async_Seq_Add_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + auto char_uuid = g_char_uuids[0].c_str(); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + // since GIO_Async_Seq_Add has not been mocked because it's a varargs + // function, we use this variable to control it's execution + g_when_shall_GIO_Async_Seq_Add_fail = g_GIO_Async_Seq_Add_call + 1; + + ///act + auto result = BLEIO_gatt_read_char_by_uuid(handle, char_uuid, on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_returns_non_zero_when_GIO_Async_Seq_Run_Async_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + auto char_uuid = g_char_uuids[0].c_str(); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((GIO_ASYNCSEQ_RESULT)GIO_ASYNCSEQ_ERROR); + + ///act + auto result = BLEIO_gatt_read_char_by_uuid(handle, char_uuid, on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + /*Tests_SRS_BLEIO_GATT_13_035: [ When an error occurs asynchronously, the value BLEIO_GATT_ERROR shall be passed for the result parameter of the on_bleio_gatt_attrib_read_complete callback. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_calls_callback_with_error_when_bluez_characteristic__proxy_new_finish_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + auto char_uuid = g_char_uuids[0].c_str(); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // create_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_sequence_error + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new(IGNORED_PTR_ARG, G_DBUS_PROXY_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + g_bluez_characteristic__proxy_new_finisher.when_shall_call_fail = 1; + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, on_read_complete(handle, NULL, BLEIO_GATT_ERROR, NULL, 0)); + + ///act + (void)BLEIO_gatt_read_char_by_uuid(handle, char_uuid, on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_029: [ BLEIO_gatt_read_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + /*Tests_SRS_BLEIO_GATT_13_035: [ When an error occurs asynchronously, the value BLEIO_GATT_ERROR shall be passed for the result parameter of the on_bleio_gatt_attrib_read_complete callback. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_calls_callback_with_error_when_g_dbus_proxy_call_finish_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + auto char_uuid = g_char_uuids[0].c_str(); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // create_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_sequence_error + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // read_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // read_characteristic_finish + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // free_context + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new(IGNORED_PTR_ARG, G_DBUS_PROXY_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__call_read_value(IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4); + + GError* expected_error = g_error_new_literal(__LINE__, -1, "Whoops!"); + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_call_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3) + .SetFailReturn((GVariant*)NULL) + .CopyOutArgumentBuffer(3, &expected_error, sizeof(GError*)); + STRICT_EXPECTED_CALL(mocks, on_read_complete(handle, NULL, BLEIO_GATT_ERROR, NULL, 0)); + + ///act + (void)BLEIO_gatt_read_char_by_uuid(handle, char_uuid, on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_030: [BLEIO_gatt_read_char_by_uuid shall return 0 (zero) if the read characteristic operation is successful.]*/ + /*Tests_SRS_BLEIO_GATT_13_032: [ BLEIO_gatt_read_char_by_uuid shall invoke on_bleio_gatt_attrib_read_complete when the read operation completes. ]*/ + /*Tests_SRS_BLEIO_GATT_13_031: [ BLEIO_gatt_read_char_by_uuid shall asynchronously initiate a read characteristic operation using the specified UUID. ]*/ + /*Tests_SRS_BLEIO_GATT_13_034: [ BLEIO_gatt_read_char_by_uuid, when successful, shall supply the data that has been read to the on_bleio_gatt_attrib_read_complete callback along with the value BLEIO_GATT_OK for the result parameter. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_succeeds) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + auto char_uuid = g_char_uuids[0].c_str(); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_new(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_bytes_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_variant_new_from_bytes(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, g_variant_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_variant_get_data_as_bytes(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_get_data(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // create_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // read_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // read_characteristic_finish + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_sequence_complete + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // free_context + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new(IGNORED_PTR_ARG, G_DBUS_PROXY_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__call_read_value(IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_call_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, on_read_complete(handle, NULL, BLEIO_GATT_OK, IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(4) + .IgnoreArgument(5); + + ///act + auto result = BLEIO_gatt_read_char_by_uuid(handle, char_uuid, on_read_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result == 0); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_030: [BLEIO_gatt_read_char_by_uuid shall return 0 (zero) if the read characteristic operation is successful.]*/ + /*Tests_SRS_BLEIO_GATT_13_032: [ BLEIO_gatt_read_char_by_uuid shall invoke on_bleio_gatt_attrib_read_complete when the read operation completes. ]*/ + /*Tests_SRS_BLEIO_GATT_13_031: [ BLEIO_gatt_read_char_by_uuid shall asynchronously initiate a read characteristic operation using the specified UUID. ]*/ + /*Tests_SRS_BLEIO_GATT_13_034: [ BLEIO_gatt_read_char_by_uuid, when successful, shall supply the data that has been read to the on_bleio_gatt_attrib_read_complete callback along with the value BLEIO_GATT_OK for the result parameter. ]*/ + /*Tests_SRS_BLEIO_GATT_13_033: [ BLEIO_gatt_read_char_by_uuid shall pass the value of the callback_context parameter to on_bleio_gatt_attrib_read_complete as the context parameter when it is invoked. ]*/ + TEST_FUNCTION(BLEIO_gatt_read_char_by_uuid_passes_context) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + auto char_uuid = g_char_uuids[0].c_str(); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_bytes_new(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_bytes_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_variant_new_from_bytes(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, g_variant_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_variant_get_data_as_bytes(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_get_data(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // create_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // read_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // read_characteristic_finish + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_sequence_complete + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // free_context + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new(IGNORED_PTR_ARG, G_DBUS_PROXY_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__call_read_value(IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_call_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, on_read_complete(handle, (void*)0x42, BLEIO_GATT_OK, IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(4) + .IgnoreArgument(5); + + ///act + auto result = BLEIO_gatt_read_char_by_uuid(handle, char_uuid, on_read_complete, (void*)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result == 0); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_036: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if bleio_gatt_handle is NULL. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_for_NULL_input1) + { + ///arrange + CBLEGATTIOMocks mocks; + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(NULL, "fake_uuid", fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_048: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if ble_uuid is NULL. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_for_NULL_input2) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, NULL, fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_037: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if on_bleio_gatt_attrib_write_complete is NULL. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_for_NULL_input3) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, "fake_uuid", fake_data, size_data, NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_045: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if buffer is NULL. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_for_NULL_input4) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, "fake_uuid", NULL, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_046: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if size is equal to 0 (zero). ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_for_zero_size) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, "fake_uuid", fake_data, 0, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_053: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an active connection to the device does not exist. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_when_not_connected) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, "fake_uuid", fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_when_g_string_new_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((GString*)NULL); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, "fake_uuid", fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_when_g_tree_lookup_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, "fake_uuid", fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_when_malloc_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + const char* char_uuid = g_char_uuids[0].c_str(); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, char_uuid, fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_when_g_bytes_new_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + const char* char_uuid = g_char_uuids[0].c_str(); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_new(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn((GBytes*)NULL); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, char_uuid, fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_when_GIO_Async_Seq_Create_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + const char* char_uuid = g_char_uuids[0].c_str(); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_new(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_bytes_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetFailReturn((GIO_ASYNCSEQ_HANDLE)NULL); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, char_uuid, fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_when_GIO_Async_Seq_Add_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + const char* char_uuid = g_char_uuids[0].c_str(); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_new(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_bytes_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + // since GIO_Async_Seq_Add has not been mocked because it's a varargs + // function, we use this variable to control it's execution + g_when_shall_GIO_Async_Seq_Add_fail = g_GIO_Async_Seq_Add_call + 1; + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, char_uuid, fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_038: [ BLEIO_gatt_write_char_by_uuid shall return a non-zero value if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_when_GIO_Async_Seq_Run_Async_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + const char* char_uuid = g_char_uuids[0].c_str(); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_new(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_bytes_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((GIO_ASYNCSEQ_RESULT)GIO_ASYNCSEQ_ERROR); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, char_uuid, fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + ASSERT_IS_TRUE(result != 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_044: [ When an error occurs asynchronously, the value BLEIO_GATT_ERROR shall be passed for the result parameter of the on_bleio_gatt_attrib_write_complete callback. ] */ + /*Tests_SRS_BLEIO_GATT_13_040: [ BLEIO_gatt_write_char_by_uuid shall asynchronously initiate a write characteristic operation using the specified UUID. ]*/ + /*Tests_SRS_BLEIO_GATT_13_041: [ BLEIO_gatt_write_char_by_uuid shall invoke on_bleio_gatt_attrib_write_complete when the write operation completes. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_when_bluez_characteristic__proxy_new_finish_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + const char* char_uuid = g_char_uuids[0].c_str(); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_new(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_bytes_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // create_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_sequence_error + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new(IGNORED_PTR_ARG, G_DBUS_PROXY_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + g_bluez_characteristic__proxy_new_finisher.when_shall_call_fail = 1; + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, NULL, BLEIO_GATT_ERROR)); + + ///act + (void)BLEIO_gatt_write_char_by_uuid(handle, char_uuid, fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_044: [ When an error occurs asynchronously, the value BLEIO_GATT_ERROR shall be passed for the result parameter of the on_bleio_gatt_attrib_write_complete callback. ] */ + /*Tests_SRS_BLEIO_GATT_13_040: [ BLEIO_gatt_write_char_by_uuid shall asynchronously initiate a write characteristic operation using the specified UUID. ]*/ + /*Tests_SRS_BLEIO_GATT_13_041: [ BLEIO_gatt_write_char_by_uuid shall invoke on_bleio_gatt_attrib_write_complete when the write operation completes. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_returns_non_zero_when_bluez_characteristic__call_write_value_finish_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + const char* char_uuid = g_char_uuids[0].c_str(); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_new(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_bytes_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_get_data(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // write_characteristic + STRICT_EXPECTED_CALL(mocks, g_variant_new_fixed_array(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG, sizeof(unsigned char))) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); // write_characteristic + STRICT_EXPECTED_CALL(mocks, g_variant_new_tuple(IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1); // write_characteristic + STRICT_EXPECTED_CALL(mocks, g_variant_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // from mock implementation of g_dbus_proxy_call + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // free_context + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // create_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // write_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // write_characteristic_finish + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_sequence_error + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_call(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, G_DBUS_CALL_FLAGS_NONE, -1, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3) + .IgnoreArgument(7) + .IgnoreArgument(8); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new(IGNORED_PTR_ARG, G_DBUS_PROXY_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + g_bluez_characteristic__call_write_value_finisher.when_shall_call_fail = 1; + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__call_write_value_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, NULL, BLEIO_GATT_ERROR)); + + ///act + (void)BLEIO_gatt_write_char_by_uuid(handle, char_uuid, fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_043: [BLEIO_gatt_write_char_by_uuid, when successful, shall supply the value BLEIO_GATT_OK for the result parameter.]*/ + /*Tests_SRS_BLEIO_GATT_13_040: [ BLEIO_gatt_write_char_by_uuid shall asynchronously initiate a write characteristic operation using the specified UUID. ]*/ + /*Tests_SRS_BLEIO_GATT_13_039: [ BLEIO_gatt_write_char_by_uuid shall return 0 (zero) if the write characteristic operation is successful. ]*/ + /*Tests_SRS_BLEIO_GATT_13_041: [ BLEIO_gatt_write_char_by_uuid shall invoke on_bleio_gatt_attrib_write_complete when the write operation completes. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_succeeds) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + const char* char_uuid = g_char_uuids[0].c_str(); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_new(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_bytes_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_get_data(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // write_characteristic + STRICT_EXPECTED_CALL(mocks, g_variant_new_fixed_array(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG, sizeof(unsigned char))) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); // write_characteristic + STRICT_EXPECTED_CALL(mocks, g_variant_new_tuple(IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1); // write_characteristic + STRICT_EXPECTED_CALL(mocks, g_variant_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // from mock implementation of g_dbus_proxy_call + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // free_context + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // create_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // write_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // write_characteristic_finish + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_sequence_complete + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_call(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, G_DBUS_CALL_FLAGS_NONE, -1, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3) + .IgnoreArgument(7) + .IgnoreArgument(8); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new(IGNORED_PTR_ARG, G_DBUS_PROXY_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__call_write_value_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, NULL, BLEIO_GATT_OK)); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, char_uuid, fake_data, size_data, on_write_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + ASSERT_IS_TRUE(result == 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_043: [ BLEIO_gatt_write_char_by_uuid, when successful, shall supply the value BLEIO_GATT_OK for the result parameter.]*/ + /*Tests_SRS_BLEIO_GATT_13_040: [ BLEIO_gatt_write_char_by_uuid shall asynchronously initiate a write characteristic operation using the specified UUID. ]*/ + /*Tests_SRS_BLEIO_GATT_13_039: [ BLEIO_gatt_write_char_by_uuid shall return 0 (zero) if the write characteristic operation is successful. ]*/ + /*Tests_SRS_BLEIO_GATT_13_041: [ BLEIO_gatt_write_char_by_uuid shall invoke on_bleio_gatt_attrib_write_complete when the write operation completes. ]*/ + /*Tests_SRS_BLEIO_GATT_13_042: [ BLEIO_gatt_write_char_by_uuid shall pass the value of the callback_context parameter to on_bleio_gatt_attrib_write_complete as the context parameter when it is invoked. ]*/ + TEST_FUNCTION(BLEIO_gatt_write_char_by_uuid_passes_context) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + const char* char_uuid = g_char_uuids[0].c_str(); + unsigned char fake_data[] = "fake data"; + size_t size_data = sizeof(fake_data) / sizeof(unsigned char); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_string_new(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_tree_lookup(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_string_free(IGNORED_PTR_ARG, TRUE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_new(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_bytes_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_bytes_get_data(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // write_characteristic + STRICT_EXPECTED_CALL(mocks, g_variant_new_fixed_array(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG, sizeof(unsigned char))) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); // write_characteristic + STRICT_EXPECTED_CALL(mocks, g_variant_new_tuple(IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1); // write_characteristic + STRICT_EXPECTED_CALL(mocks, g_variant_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // from mock implementation of g_dbus_proxy_call + STRICT_EXPECTED_CALL(mocks, g_object_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // free_context + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_Run_Async(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // create_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // write_characteristic + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // write_characteristic_finish + STRICT_EXPECTED_CALL(mocks, GIO_Async_Seq_GetContext(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_sequence_complete + STRICT_EXPECTED_CALL(mocks, g_dbus_proxy_call(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, G_DBUS_CALL_FLAGS_NONE, -1, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3) + .IgnoreArgument(7) + .IgnoreArgument(8); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new(IGNORED_PTR_ARG, G_DBUS_PROXY_FLAGS_NONE, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(6) + .IgnoreArgument(7); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__proxy_new_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, bluez_characteristic__call_write_value_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, (void*)0x42, BLEIO_GATT_OK)); + + ///act + auto result = BLEIO_gatt_write_char_by_uuid(handle, char_uuid, fake_data, size_data, on_write_complete, (void*)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_TRUE(g_was_GIO_Async_Seq_Add_called); + ASSERT_IS_TRUE(result == 0); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_016: [ BLEIO_gatt_disconnect shall do nothing if bleio_gatt_handle is NULL. ]*/ + TEST_FUNCTION(BLEIO_gatt_disconnect_does_nothing_for_NULL_input1) + { + ///arrange + CBLEGATTIOMocks mocks; + + ///act + BLEIO_gatt_disconnect(NULL, on_disconnect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_GATT_13_015: [ BLEIO_gatt_disconnect shall do nothing if an open connection does not exist when it is called. ]*/ + TEST_FUNCTION(BLEIO_gatt_disconnect_does_nothing_when_not_connected) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + mocks.ResetAllCalls(); + + ///act + BLEIO_gatt_disconnect(handle, on_disconnect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_054: [ BLEIO_gatt_disconnect shall do nothing if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_gatt_disconnect_does_nothing_when_malloc_fails) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + BLEIO_gatt_disconnect(handle, on_disconnect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_014: [ BLEIO_gatt_disconnect shall asynchronously disconnect from the BLE device if an open connection exists. ]*/ + /*Tests_SRS_BLEIO_GATT_13_050: [ When the disconnect operation has been completed, the callback function pointed at by on_bleio_gatt_disconnect_complete shall be invoked if it is not NULL. ]*/ + TEST_FUNCTION(BLEIO_gatt_disconnect_completes) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, bluez_device__call_disconnect(IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, bluez_device__call_disconnect_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, on_disconnect_complete(handle, NULL)); + + ///act + BLEIO_gatt_disconnect(handle, on_disconnect_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + + /*Tests_SRS_BLEIO_GATT_13_014: [ BLEIO_gatt_disconnect shall asynchronously disconnect from the BLE device if an open connection exists. ]*/ + /*Tests_SRS_BLEIO_GATT_13_050: [ When the disconnect operation has been completed, the callback function pointed at by on_bleio_gatt_disconnect_complete shall be invoked if it is not NULL. ]*/ + /*Tests_SRS_BLEIO_GATT_13_049: [ When on_bleio_gatt_disconnect_complete is invoked the value passed in callback_context to BLEIO_gatt_disconnect shall be passed along to on_bleio_gatt_disconnect_complete. ]*/ + TEST_FUNCTION(BLEIO_gatt_disconnect_passes_context) + { + ///arrange + CBLEGATTIOMocks mocks; + auto handle = BLEIO_gatt_create(&g_device_config); + (void)BLEIO_gatt_connect(handle, on_gatt_connect_complete, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, bluez_device__call_disconnect(IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, bluez_device__call_disconnect_finish(IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, on_disconnect_complete(handle, (void*)0x42)); + + ///act + BLEIO_gatt_disconnect(handle, on_disconnect_complete, (void*)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_gatt_destroy(handle); + } + +END_TEST_SUITE(ble_gatt_io_unittests) diff --git a/modules/ble/tests/ble_gatt_io_unittests/ble_gatt_io_unittests_windows.cpp b/modules/ble/tests/ble_gatt_io_unittests/ble_gatt_io_unittests_windows.cpp new file mode 100644 index 00000000..3491eaca --- /dev/null +++ b/modules/ble/tests/ble_gatt_io_unittests/ble_gatt_io_unittests_windows.cpp @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +/*general macros useful for MOCKS*/ +#define CURRENT_API_CALL(API) C2(C2(current, API), _call) +#define WHEN_SHALL_API_FAIL(API) C2(C2(whenShall, API), _fail) +#define DEFINE_FAIL_VARIABLES(API) static size_t CURRENT_API_CALL(API); static size_t WHEN_SHALL_API_FAIL(API); +#define MAKE_FAIL(API, WHEN) do{WHEN_SHALL_API_FAIL(API) = CURRENT_API_CALL(API) + WHEN;} while(0) +#define RESET_API_COUNTERS(API) CURRENT_API_CALL(API) = WHEN_SHALL_API_FAIL(API) = 0; + +#define LIST_OF_COUNTED_APIS \ + gballoc_malloc \ + +FOR_EACH_1(DEFINE_FAIL_VARIABLES, LIST_OF_COUNTED_APIS) + +#include "azure_c_shared_utility/lock.h" + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit +}; + +TYPED_MOCK_CLASS(CBLEGATTIOMocks, CGlobalMock) +{ +public: + + // memory + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEGATTIOMocks, , void, gballoc_free, void*, ptr); + +static void mocks_ResetAllCounters(void) +{ + FOR_EACH_1(RESET_API_COUNTERS, LIST_OF_COUNTED_APIS); +} + +BEGIN_TEST_SUITE(ble_gatt_io_unittests) + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + + mocks_ResetAllCounters(); + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + } + + TEST_FUNCTION(BLEGATTIO_noop_test) + { + ///arrange + CBLEGATTIOMocks mocks; + + ///act + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } +END_TEST_SUITE(ble_gatt_io_unittests) diff --git a/modules/ble/tests/ble_gatt_io_unittests/main.c b/modules/ble/tests/ble_gatt_io_unittests/main.c new file mode 100644 index 00000000..103f6558 --- /dev/null +++ b/modules/ble/tests/ble_gatt_io_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(ble_gatt_io_unittests, failedTestCount); + return failedTestCount; +} diff --git a/modules/ble/tests/ble_hl_unittests/CMakeLists.txt b/modules/ble/tests/ble_hl_unittests/CMakeLists.txt new file mode 100644 index 00000000..78274f2e --- /dev/null +++ b/modules/ble/tests/ble_hl_unittests/CMakeLists.txt @@ -0,0 +1,44 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for ble_hl_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName ble_hl_unittests) + +set(${theseTestsName}_cpp_files + ${theseTestsName}.cpp +) + +set(ble_hl_test_sources + ../../src/ble_utils.c + ../../src/ble_hl.c +) + +if(LINUX) + # Include GIO headers/libs + include_directories(${GIOUNIX_INCLUDE_DIRS}) + set(LIBS ${GIOUNIX_LIBRARIES}) +endif() + +set(ble_hl_test_headers + ../../inc/ble_utils.h + ../../inc/ble_hl.h +) + +include_directories( + ../../inc + ${GW_SRC} + ${GW_INC} +) + +set(${theseTestsName}_c_files + ${ble_hl_test_sources} +) + +set(${theseTestsName}_h_files + ${ble_hl_test_headers} +) + +build_test_artifacts(${theseTestsName} ON ${LIBS}) diff --git a/modules/ble/tests/ble_hl_unittests/ble_hl_unittests.cpp b/modules/ble/tests/ble_hl_unittests/ble_hl_unittests.cpp new file mode 100644 index 00000000..61d19d09 --- /dev/null +++ b/modules/ble/tests/ble_hl_unittests/ble_hl_unittests.cpp @@ -0,0 +1,2804 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/base64.h" +#include "ble.h" +#include "ble_hl.h" +#include "message.h" +#include "azure_c_shared_utility/constmap.h" +#include "azure_c_shared_utility/map.h" +#include "messageproperties.h" + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +/*these are simple cached variables*/ +static pfModule_Create BLE_HL_Create = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Destroy BLE_HL_Destroy = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Receive BLE_HL_Receive = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ + +static size_t gMessageSize; +static const unsigned char * gMessageSource; + +#define FAKE_CONFIG "" \ +"{" \ +" \"modules\": [" \ +" {" \ +" \"module name\": \"BLE Printer\"," \ +" \"module path\": \"/ble_printer.so\"," \ +" \"args\": \"\"" \ +" }," \ +" {" \ +" \"module name\": \"SensorTag\"," \ +" \"module path\": \"/ble_hl.so\"," \ +" \"args\": {" \ +" \"controller_index\": 0," \ +" \"device_mac_address\": \"AA:BB:CC:DD:EE:FF\"," \ +" \"instructions\": [" \ +" {" \ +" \"type\": \"read_once\"," \ +" \"characteristic_uuid\": \"00002A24-0000-1000-8000-00805F9B34FB\"" \ +" }," \ +" {" \ +" \"type\": \"write_at_init\"," \ +" \"characteristic_uuid\": \"F000AA02-0451-4000-B000-000000000000\"," \ +" \"data\": \"AQ==\"" \ +" }," \ +" {" \ +" \"type\": \"read_periodic\"," \ +" \"characteristic_uuid\": \"F000AA01-0451-4000-B000-000000000000\"," \ +" \"interval_in_ms\": 1000" \ +" }," \ +" {" \ +" \"type\": \"write_at_exit\"," \ +" \"characteristic_uuid\": \"F000AA02-0451-4000-B000-000000000000\"," \ +" \"data\": \"AA==\"" \ +" }" \ +" ]" \ +" }" \ +" }" \ +" ]" \ +"}" + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit + +#include /* size_t */ + + /* Types and enums */ + typedef struct json_object_t JSON_Object; + typedef struct json_array_t JSON_Array; + typedef struct json_value_t JSON_Value; + + enum json_value_type { + JSONError = -1, + JSONNull = 1, + JSONString = 2, + JSONNumber = 3, + JSONObject = 4, + JSONArray = 5, + JSONBoolean = 6 + }; + typedef int JSON_Value_Type; + + enum json_result_t { + JSONSuccess = 0, + JSONFailure = -1 + }; + typedef int JSON_Status; + + typedef void * (*JSON_Malloc_Function)(size_t); + typedef void(*JSON_Free_Function)(void *); + + /* Call only once, before calling any other function from parson API. If not called, malloc and free + from stdlib will be used for all allocations */ + void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun); + + /* Parses first JSON value in a file, returns NULL in case of error */ + JSON_Value * json_parse_file(const char *filename); + + /* Parses first JSON value in a file and ignores comments (/ * * / and //), + returns NULL in case of error */ + JSON_Value * json_parse_file_with_comments(const char *filename); + + /* Parses first JSON value in a string, returns NULL in case of error */ + JSON_Value * json_parse_string(const char *string); + + /* Parses first JSON value in a string and ignores comments (/ * * / and //), + returns NULL in case of error */ + JSON_Value * json_parse_string_with_comments(const char *string); + + /* Serialization */ + size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */ + JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); + JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename); + char * json_serialize_to_string(const JSON_Value *value); + + /* Pretty serialization */ + size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */ + JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); + JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename); + char * json_serialize_to_string_pretty(const JSON_Value *value); + + void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */ + + /* Comparing */ + int json_value_equals(const JSON_Value *a, const JSON_Value *b); + + /* Validation + This is *NOT* JSON Schema. It validates json by checking if object have identically + named fields with matching types. + For example schema {"name":"", "age":0} will validate + {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"}, + but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}. + In case of arrays, only first value in schema is checked against all values in tested array. + Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays, + null validates values of every type. + */ + JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value); + + /* + * JSON Object + */ + JSON_Value * json_object_get_value(const JSON_Object *object, const char *name); + const char * json_object_get_string(const JSON_Object *object, const char *name); + JSON_Object * json_object_get_object(const JSON_Object *object, const char *name); + JSON_Array * json_object_get_array(const JSON_Object *object, const char *name); + double json_object_get_number(const JSON_Object *object, const char *name); /* returns 0 on fail */ + int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + + /* dotget functions enable addressing values with dot notation in nested objects, + just like in structs or c++/java/c# objects (e.g. objectA.objectB.value). + Because valid names in JSON can contain dots, some values may be inaccessible + this way. */ + JSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name); + const char * json_object_dotget_string(const JSON_Object *object, const char *name); + JSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name); + JSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name); + double json_object_dotget_number(const JSON_Object *object, const char *name); /* returns 0 on fail */ + int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + + /* Functions to get available names */ + size_t json_object_get_count(const JSON_Object *object); + const char * json_object_get_name(const JSON_Object *object, size_t index); + + /* Creates new name-value pair or frees and replaces old value with a new one. + * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */ + JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value); + JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string); + JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number); + JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean); + JSON_Status json_object_set_null(JSON_Object *object, const char *name); + + /* Works like dotget functions, but creates whole hierarchy if necessary. + * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */ + JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value); + JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string); + JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number); + JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean); + JSON_Status json_object_dotset_null(JSON_Object *object, const char *name); + + /* Frees and removes name-value pair */ + JSON_Status json_object_remove(JSON_Object *object, const char *name); + + /* Works like dotget function, but removes name-value pair only on exact match. */ + JSON_Status json_object_dotremove(JSON_Object *object, const char *key); + + /* Removes all name-value pairs in object */ + JSON_Status json_object_clear(JSON_Object *object); + + /* + *JSON Array + */ + JSON_Value * json_array_get_value(const JSON_Array *array, size_t index); + const char * json_array_get_string(const JSON_Array *array, size_t index); + JSON_Object * json_array_get_object(const JSON_Array *array, size_t index); + JSON_Array * json_array_get_array(const JSON_Array *array, size_t index); + double json_array_get_number(const JSON_Array *array, size_t index); /* returns 0 on fail */ + int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */ + size_t json_array_get_count(const JSON_Array *array); + + /* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist. + * Order of values in array may change during execution. */ + JSON_Status json_array_remove(JSON_Array *array, size_t i); + + /* Frees and removes from array value at given index and replaces it with given one. + * Does nothing and returns JSONFailure if index doesn't exist. + * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */ + JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value); + JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string); + JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number); + JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean); + JSON_Status json_array_replace_null(JSON_Array *array, size_t i); + + /* Frees and removes all values from array */ + JSON_Status json_array_clear(JSON_Array *array); + + /* Appends new value at the end of array. + * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */ + JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value); + JSON_Status json_array_append_string(JSON_Array *array, const char *string); + JSON_Status json_array_append_number(JSON_Array *array, double number); + JSON_Status json_array_append_boolean(JSON_Array *array, int boolean); + JSON_Status json_array_append_null(JSON_Array *array); + + /* + *JSON Value + */ + JSON_Value * json_value_init_object(void); + JSON_Value * json_value_init_array(void); + JSON_Value * json_value_init_string(const char *string); /* copies passed string */ + JSON_Value * json_value_init_number(double number); + JSON_Value * json_value_init_boolean(int boolean); + JSON_Value * json_value_init_null(void); + JSON_Value * json_value_deep_copy(const JSON_Value *value); + void json_value_free(JSON_Value *value); + + JSON_Value_Type json_value_get_type(const JSON_Value *value); + JSON_Object * json_value_get_object(const JSON_Value *value); + JSON_Array * json_value_get_array(const JSON_Value *value); + const char * json_value_get_string(const JSON_Value *value); + double json_value_get_number(const JSON_Value *value); + int json_value_get_boolean(const JSON_Value *value); + + /* Same as above, but shorter */ + JSON_Value_Type json_type(const JSON_Value *value); + JSON_Object * json_object(const JSON_Value *value); + JSON_Array * json_array(const JSON_Value *value); + const char * json_string(const JSON_Value *value); + double json_number(const JSON_Value *value); + int json_boolean(const JSON_Value *value); + +#define parson_parson_h +#ifdef _CRT_SECURE_NO_WARNINGS + #undef _CRT_SECURE_NO_WARNINGS + #include "parson.c" + #define _CRT_SECURE_NO_WARNINGS +#else + #include "parson.c" +#endif + +#include "buffer.c" +#include "vector.c" +#include "strings.c" +}; + +#undef parson_parson_h +#include "parson.h" + +/*forward declarations*/ +MODULE_HANDLE BLE_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +/*this destroys (frees resources) of the module parameter*/ +void BLE_Destroy(MODULE_HANDLE moduleHandle); +/*this is the module's callback function - gets called when a message is to be received by the module*/ +void BLE_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); + +static MODULE_APIS BLE_APIS = +{ + BLE_Create, + BLE_Destroy, + BLE_Receive +}; + +TYPED_MOCK_CLASS(CBLEHLMocks, CGlobalMock) +{ +public: + MOCK_STATIC_METHOD_1(, VECTOR_HANDLE, VECTOR_create, size_t, elementSize) + auto result2 = BASEIMPLEMENTATION::VECTOR_create(elementSize); + MOCK_METHOD_END(VECTOR_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, VECTOR_destroy, VECTOR_HANDLE, vector) + BASEIMPLEMENTATION::VECTOR_destroy(vector); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, size_t, VECTOR_size, VECTOR_HANDLE, vector) + size_t result2 = BASEIMPLEMENTATION::VECTOR_size(vector); + MOCK_METHOD_END(size_t, result2) + + MOCK_STATIC_METHOD_2(, void*, VECTOR_element, VECTOR_HANDLE, vector, size_t, index) + void* result2 = BASEIMPLEMENTATION::VECTOR_element(vector, index); + MOCK_METHOD_END(void*, result2) + + MOCK_STATIC_METHOD_3(, int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements) + auto result2 = BASEIMPLEMENTATION::VECTOR_push_back(handle, elements, numElements); + MOCK_METHOD_END(int, result2) + + MOCK_STATIC_METHOD_1(, STRING_HANDLE, STRING_construct, const char*, source) + auto result2 = BASEIMPLEMENTATION::STRING_construct(source); + MOCK_METHOD_END(STRING_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, const char*, STRING_c_str, STRING_HANDLE, handle) + MOCK_METHOD_END(const char*, BASEIMPLEMENTATION::STRING_c_str(handle)) + + MOCK_STATIC_METHOD_1(, void, STRING_delete, STRING_HANDLE, handle) + BASEIMPLEMENTATION::STRING_delete(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, void, BUFFER_delete, BUFFER_HANDLE, handle) + BASEIMPLEMENTATION::BUFFER_delete(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, BUFFER_HANDLE, Base64_Decoder, const char*, source) + auto result2 = BASEIMPLEMENTATION::BUFFER_create((const unsigned char*)"abc", 3); + MOCK_METHOD_END(BUFFER_HANDLE, result2) + + // memory + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + /*Parson Mocks*/ + MOCK_STATIC_METHOD_1(, JSON_Value*, json_parse_string, const char *, filename) + JSON_Value* value = NULL; + if (filename != NULL) + { + value = (JSON_Value*)malloc(sizeof(BASEIMPLEMENTATION::JSON_Value)); + } + MOCK_METHOD_END(JSON_Value*, value); + + MOCK_STATIC_METHOD_1(, JSON_Value*, json_parse_file, const char *, filename) + JSON_Value* value = NULL; + if (filename != NULL) + { + value = (JSON_Value*)malloc(sizeof(BASEIMPLEMENTATION::JSON_Value)); + } + MOCK_METHOD_END(JSON_Value*, value); + + MOCK_STATIC_METHOD_1(, JSON_Object*, json_value_get_object, const JSON_Value*, value) + JSON_Object* object = NULL; + if (value != NULL) + { + object = (JSON_Object*)0x42; + } + MOCK_METHOD_END(JSON_Object*, object); + + MOCK_STATIC_METHOD_2(, JSON_Array*, json_object_get_array, const JSON_Object*, object, const char*, name) + JSON_Array* arr = NULL; + if (object != NULL && name != NULL) + { + arr = (JSON_Array*)0x42; + } + MOCK_METHOD_END(JSON_Array*, arr); + + MOCK_STATIC_METHOD_1(, size_t, json_array_get_count, const JSON_Array*, arr) + size_t size = 4; + MOCK_METHOD_END(size_t, size); + + MOCK_STATIC_METHOD_2(, double, json_object_get_number, const JSON_Object *, object, const char *, name) + double result2 = 0; + MOCK_METHOD_END(double, result2); + + MOCK_STATIC_METHOD_2(, JSON_Object*, json_array_get_object, const JSON_Array*, arr, size_t, index) + JSON_Object* object = NULL; + if (arr != NULL && index >= 0) + { + object = (JSON_Object*)0x42; + } + MOCK_METHOD_END(JSON_Object*, object); + + MOCK_STATIC_METHOD_2(, const char*, json_object_get_string, const JSON_Object*, object, const char*, name) + const char* result2; + if(strcmp(name, "device_mac_address") == 0) + { + result2 = "AA:BB:CC:DD:EE:FF"; + } + else if(strcmp(name, "data") == 0) + { + result2 = "AA=="; + } + else if(strcmp(name, "type") == 0) + { + result2 = "read_once"; + } + else if(strcmp(name, "characteristic_uuid") == 0) + { + result2 = "00002A24-0000-1000-8000-00805F9B34FB"; + } + else + { + result2 = NULL; + } + MOCK_METHOD_END(const char*, result2); + + MOCK_STATIC_METHOD_2(, JSON_Value*, json_object_get_value, const JSON_Object*, object, const char*, name) + JSON_Value* value = NULL; + if (object != NULL && name != NULL) + { + value = (JSON_Value*)0x42; + } + MOCK_METHOD_END(JSON_Value*, value); + + MOCK_STATIC_METHOD_1(, void, json_value_free, JSON_Value*, value) + free(value); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_0(, const MODULE_APIS*, MODULE_STATIC_GETAPIS(BLE_MODULE)) + MOCK_METHOD_END(const MODULE_APIS*, (const MODULE_APIS*)&BLE_APIS); + + MOCK_STATIC_METHOD_2(, MODULE_HANDLE, BLE_Create, MESSAGE_BUS_HANDLE, busHandle, const void*, configuration) + BLE_CONFIG *config = (BLE_CONFIG*)malloc(sizeof(BLE_CONFIG)); + memcpy(config, configuration, sizeof(BLE_CONFIG)); + MODULE_HANDLE result2 = (MODULE_HANDLE)config; + MOCK_METHOD_END(MODULE_HANDLE, result2); + + MOCK_STATIC_METHOD_1(, void, BLE_Destroy, MODULE_HANDLE, moduleHandle); + { + if(moduleHandle != NULL) + { + BLE_CONFIG* config = (BLE_CONFIG*)moduleHandle; + if(config->instructions != NULL) + { + size_t len = VECTOR_size(config->instructions); + for (size_t i = 0; i < len; i++) + { + BLE_INSTRUCTION* instr = (BLE_INSTRUCTION*)VECTOR_element(config->instructions, i); + + // free resources + if (instr->characteristic_uuid != NULL) + { + STRING_delete(instr->characteristic_uuid); + } + if ( + ( + instr->instruction_type == WRITE_AT_INIT + || + instr->instruction_type == WRITE_AT_EXIT + || + instr->instruction_type == WRITE_ONCE + ) + && + instr->data.buffer != NULL + ) + { + BUFFER_delete(instr->data.buffer); + } + } + + VECTOR_destroy(config->instructions); + } + + free(config); + } + } + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, void, BLE_Receive, MODULE_HANDLE, moduleHandle, MESSAGE_HANDLE, messageHandle) + if (moduleHandle != NULL) + { + BLE_CONFIG* config = (BLE_CONFIG*)moduleHandle; + if (config->instructions == NULL) + { + config->instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + } + if (config->instructions != NULL) + { + BLE_INSTRUCTION* instr = (BLE_INSTRUCTION*)gMessageSource; + if (instr != NULL) + { + BASEIMPLEMENTATION::VECTOR_push_back(config->instructions, instr, 1); + } + } + } + + MOCK_VOID_METHOD_END() + + // Message + + MOCK_STATIC_METHOD_1(, MESSAGE_HANDLE, Message_Create, const MESSAGE_CONFIG*, cfg) + gMessageSize = cfg->size; + gMessageSource = cfg->source; + MOCK_METHOD_END(MESSAGE_HANDLE, (MESSAGE_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(1)) + + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message) + MOCK_METHOD_END(CONSTMAP_HANDLE, (CONSTMAP_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(1)) + + MOCK_STATIC_METHOD_1(, const CONSTBUFFER*, Message_GetContent, MESSAGE_HANDLE, message) + MOCK_METHOD_END(const CONSTBUFFER*, (const CONSTBUFFER*)NULL); + + MOCK_STATIC_METHOD_1(, void, Message_Destroy, MESSAGE_HANDLE, message) + BASEIMPLEMENTATION::gballoc_free(message); + MOCK_VOID_METHOD_END() + + // CONSTMAP + + MOCK_STATIC_METHOD_1(, MAP_HANDLE, ConstMap_CloneWriteable, CONSTMAP_HANDLE, handle) + MOCK_METHOD_END(MAP_HANDLE, (MAP_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(1)) + + MOCK_STATIC_METHOD_2(, const char*, ConstMap_GetValue, CONSTMAP_HANDLE, handle, const char*, key) + MOCK_METHOD_END(const char*, (const char*)NULL) + + MOCK_STATIC_METHOD_1(, void, ConstMap_Destroy, CONSTMAP_HANDLE, handle) + BASEIMPLEMENTATION::gballoc_free(handle); + MOCK_VOID_METHOD_END() + + //MAP + MOCK_STATIC_METHOD_3(, MAP_RESULT, Map_AddOrUpdate, MAP_HANDLE, handle, const char*, key, const char*, value) + MOCK_METHOD_END(MAP_RESULT, MAP_OK) + + MOCK_STATIC_METHOD_1(, void, Map_Destroy, MAP_HANDLE, handle) + MOCK_VOID_METHOD_END() +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , JSON_Value*, json_parse_string, const char *, filename); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , JSON_Object*, json_value_get_object, const JSON_Value*, value); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEHLMocks, , double, json_object_get_number, const JSON_Object*, value, const char*, name); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEHLMocks, , const char*, json_object_get_string, const JSON_Object*, object, const char*, name); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEHLMocks, , JSON_Array*, json_object_get_array, const JSON_Object*, object, const char*, name); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , void, json_value_free, JSON_Value*, value); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , size_t, json_array_get_count, const JSON_Array*, arr); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEHLMocks, , JSON_Object*, json_array_get_object, const JSON_Array*, arr, size_t, index); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , void, gballoc_free, void*, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEHLMocks, , MODULE_HANDLE, BLE_Create, MESSAGE_BUS_HANDLE, busHandle, const void*, configuration); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , void, BLE_Destroy, MODULE_HANDLE, moduleHandle); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEHLMocks, , void, BLE_Receive, MODULE_HANDLE, moduleHandle, MESSAGE_HANDLE, messageHandle); +DECLARE_GLOBAL_MOCK_METHOD_0(CBLEHLMocks, , const MODULE_APIS*, MODULE_STATIC_GETAPIS(BLE_MODULE)); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , VECTOR_HANDLE, VECTOR_create, size_t, elementSize); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , void, VECTOR_destroy, VECTOR_HANDLE, vector); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , size_t, VECTOR_size, VECTOR_HANDLE, vector); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEHLMocks, , void*, VECTOR_element, VECTOR_HANDLE, vector, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEHLMocks, , int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , BUFFER_HANDLE, Base64_Decoder, const char*, source); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , STRING_HANDLE, STRING_construct, const char*, source); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , const char*, STRING_c_str, STRING_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , void, STRING_delete, STRING_HANDLE, handle); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , void, BUFFER_delete, BUFFER_HANDLE, handle); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , MESSAGE_HANDLE, Message_Create, const MESSAGE_CONFIG*, cfg); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , const CONSTBUFFER*, Message_GetContent, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , void, Message_Destroy, MESSAGE_HANDLE, message); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , MAP_HANDLE, ConstMap_CloneWriteable, CONSTMAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEHLMocks, , const char*, ConstMap_GetValue, CONSTMAP_HANDLE, handle, const char*, key); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , void, ConstMap_Destroy, CONSTMAP_HANDLE, handle); + +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEHLMocks, , MAP_RESULT, Map_AddOrUpdate, MAP_HANDLE, handle, const char*, key, const char*, value); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEHLMocks, , void, Map_Destroy, MAP_HANDLE, handle); + +BEGIN_TEST_SUITE(ble_hl_unittests) + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + + BLE_HL_Create = Module_GetAPIS()->Module_Create; + BLE_HL_Destroy = Module_GetAPIS()->Module_Destroy; + BLE_HL_Receive = Module_GetAPIS()->Module_Receive; + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + } + + /*Tests_SRS_BLE_HL_13_001: [ BLE_HL_Create shall return NULL if the bus or configuration parameters are NULL. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_bus_is_NULL) + { + ///arrange + CBLEHLMocks mocks; + + ///act + auto result = BLE_HL_Create(NULL, (const void*)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_001: [ BLE_HL_Create shall return NULL if the bus or configuration parameters are NULL. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_configuration_is_NULL) + { + ///arrange + CBLEHLMocks mocks; + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_002: [ BLE_HL_Create shall return NULL if any of the underlying platform calls fail. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_json_parse_string_fails) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((JSON_Value*)NULL); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_003: [ BLE_HL_Create shall return NULL if the JSON does not start with an object. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_json_value_get_object_for_root_fails) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((JSON_Object*)NULL); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_005: [ BLE_HL_Create shall return NULL if the controller_index value in the JSON is less than zero. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_controller_index_is_negative) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1) + .SetFailReturn((int)-1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_004: [ BLE_HL_Create shall return NULL if there is no device_mac_address property in the JSON. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_mac_address_is_NULL) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1) + .SetFailReturn((const char*)NULL); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_006: [ BLE_HL_Create shall return NULL if the instructions array does not exist in the JSON. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_instructions_is_NULL) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1) + .SetFailReturn((JSON_Array *)NULL); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_020: [ BLE_HL_Create shall return NULL if the instructions array length is equal to zero. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_instructions_is_empty) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)0); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_002: [ BLE_HL_Create shall return NULL if any of the underlying platform calls fail. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_VECTOR_create_fails) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1) + .SetFailReturn((VECTOR_HANDLE)NULL); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_007: [ BLE_HL_Create shall return NULL if each instruction is not an object. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_instruction_entry_is_NULL) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1) + .SetFailReturn((JSON_Object*)NULL); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_008: [ BLE_HL_Create shall return NULL if a given instruction does not have a type property. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_instruction_type_is_NULL) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetFailReturn((const char*)NULL); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_009: [ BLE_HL_Create shall return NULL if a given instruction does not have a characteristic_uuid property. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_instruction_characteristic_is_NULL) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1) + .SetFailReturn((const char*)NULL); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_002: [ BLE_HL_Create shall return NULL if any of the underlying platform calls fail. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_STRING_construct_fails) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((STRING_HANDLE)NULL); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_010: [ BLE_HL_Create shall return NULL if the interval_in_ms value for a read_periodic instruction isn't greater than zero. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_read_periodic_interval_is_zero) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"read_periodic"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "interval_in_ms")) + .IgnoreArgument(1) + .SetReturn((uint32_t)0); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_011: [ BLE_HL_Create shall return NULL if an instruction of type write_at_init or write_at_exit does not have a data property. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_write_instr_data_is_NULL) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1) + .SetFailReturn((const char*)NULL); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_012: [ BLE_HL_Create shall return NULL if an instruction of type write_at_init or write_at_exit has a data property whose value does not decode successfully from base 64. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_write_instr_base64_decode_fails) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((BUFFER_HANDLE)NULL); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_021: [ BLE_HL_Create shall return NULL if a given instruction's type property is unrecognized. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_instr_type_is_unknown) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"booyah_yah"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_002: [ BLE_HL_Create shall return NULL if any of the underlying platform calls fail. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_VECTOR_push_back_fails) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn((int)__LINE__); + + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_002: [ BLE_HL_Create shall return NULL if any of the underlying platform calls fail. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_and_frees_first_instr_when_second_fails) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + // cause the second VECTOR_push_back to fail + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn((int)__LINE__); + + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_exit"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + // return 2 instructions + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)2); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_013: [ BLE_HL_Create shall return NULL if the device_mac_address property's value is not a well-formed MAC address. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_mac_address_is_invalid) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1) + .SetReturn((const char*)"no mac address here"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_013: [ BLE_HL_Create shall return NULL if the device_mac_address property's value is not a well-formed MAC address. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_mac_address_is_invalid2) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1) + .SetReturn((const char*)"AA-BB-CC-DD-EE:FF"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_002: [ BLE_HL_Create shall return NULL if any of the underlying platform calls fail. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_malloc_fails) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_022: [ BLE_HL_Create shall return NULL if calling the underlying module's create function fails. ]*/ + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_low_level_module_create_fails) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(BLE_MODULE)()); + + STRICT_EXPECTED_CALL(mocks, BLE_Create((MESSAGE_BUS_HANDLE)0x42, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .SetFailReturn((MODULE_HANDLE)NULL); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + TEST_FUNCTION(BLE_HL_Create_returns_NULL_when_macAddr_string_create_fails) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((STRING_HANDLE)NULL); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + + } + /*Tests_SRS_BLE_HL_13_014: [ BLE_HL_Create shall call the underlying module's 'create' function. ]*/ + /*Tests_SRS_BLE_HL_13_023: [ BLE_HL_Create shall return a non-NULL handle if calling the underlying module's create function succeeds. ]*/ + TEST_FUNCTION(BLE_HL_Create_succeeds) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(BLE_MODULE)()); + + STRICT_EXPECTED_CALL(mocks, BLE_Create((MESSAGE_BUS_HANDLE)0x42, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLE_INSTRUCTION))) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_number(IGNORED_PTR_ARG, "controller_index")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "device_mac_address")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_array(IGNORED_PTR_ARG, "instructions")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + ///act + auto result = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + BLE_HL_Destroy(result); + } + + /*Tests_SRS_BLE_HL_13_017: [ BLE_HL_Destroy shall do nothing if module is NULL. ]*/ + TEST_FUNCTION(BLE_HL_Destroy_does_nothing_with_null_input) + { + ///arrange + CBLEHLMocks mocks; + + ///act + BLE_HL_Destroy(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_015: [ BLE_HL_Destroy shall destroy all used resources. ]*/ + TEST_FUNCTION(BLE_HL_Destroy_frees_resources) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(BLE_MODULE)()); + + STRICT_EXPECTED_CALL(mocks, BLE_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Destroy(module); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLE_HL_13_018: [ BLE_HL_Receive shall forward the call to the underlying module. ]*/ + //Tests_SRS_BLE_HL_17_006: [ BLE_HL_Receive shall parse the message contents as a JSON object. ] + //Tests_SRS_BLE_HL_17_016: [ BLE_HL_Receive shall set characteristic_uuid to the created STRING. ] + //Tests_SRS_BLE_HL_17_014: [ BLE_HL_Receive shall parse the json object to fill in a new BLE_INSTRUCTION. ] + //Tests_SRS_BLE_HL_17_018: [ BLE_HL_Receive shall call ConstMap_CloneWriteable on the message properties. ] + //Tests_SRS_BLE_HL_17_020: [ BLE_HL_Receive shall call Map_AddOrUpdate with key of "source" and value of "BLE". ] + //Tests_SRS_BLE_HL_17_023: [ BLE_HL_Receive shall create a new message by calling Message_Create with new map and BLE_INSTRUCTION as the buffer. ] + //Tests_SRS_BLE_HL_17_025: [ BLE_HL_Receive shall free all resources created. ] + TEST_FUNCTION(BLE_HL_Receive_forwards_call) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)GW_IDMAP_MODULE); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(fakeMessage)) + .SetReturn((const CONSTBUFFER *)&messageBuffer); + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn("write_once"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1) + .SetReturn("F000AA02-0451-4000-B000-000000000000"); + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((MAP_HANDLE)0x42); + STRICT_EXPECTED_CALL(mocks, Map_Destroy((MAP_HANDLE)0x42)); + + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_COMMAND)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_Create(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(BLE_MODULE)()); + STRICT_EXPECTED_CALL(mocks, BLE_Receive(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_024: [ If creating new message fails, BLE_HL_Receive shall deallocate all resources and return. ] + TEST_FUNCTION(BLE_HL_Receive_message_create_fails) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)GW_IDMAP_MODULE); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(fakeMessage)) + .SetReturn((const CONSTBUFFER *)&messageBuffer); + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn("write_once"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1) + .SetReturn("F000AA02-0451-4000-B000-000000000000"); + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((MAP_HANDLE)0x42); + STRICT_EXPECTED_CALL(mocks, Map_Destroy((MAP_HANDLE)0x42)); + + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_COMMAND)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_Create(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((MESSAGE_HANDLE)NULL); + + + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + TEST_FUNCTION(BLE_HL_Receive_map_update_fails) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)GW_IDMAP_MODULE); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(fakeMessage)) + .SetReturn((const CONSTBUFFER *)&messageBuffer); + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn("write_once"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1) + .SetReturn("F000AA02-0451-4000-B000-000000000000"); + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((MAP_HANDLE)0x42); + STRICT_EXPECTED_CALL(mocks, Map_Destroy((MAP_HANDLE)0x42)); + + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_COMMAND)) + .IgnoreArgument(1) + .SetFailReturn(MAP_ERROR); + + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_019: [ If ConstMap_CloneWriteable fails, BLE_HL_Receive shall return. ] + TEST_FUNCTION(BLE_HL_Receive_constmap_clonewriteable_fails) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)GW_IDMAP_MODULE); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(fakeMessage)) + .SetReturn((const CONSTBUFFER *)&messageBuffer); + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn("write_once"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1) + .SetReturn("F000AA02-0451-4000-B000-000000000000"); + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((MAP_HANDLE)NULL); + + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_008: [ BLE_HL_Receive shall return if the JSON object does not contain the following fields: "type", "characteristic_uuid", and "data". ] + //Tests_SRS_BLE_HL_17_026: [ If the json object does not parse, BLE_HL_Receive shall return. ] + TEST_FUNCTION(BLE_HL_Receive_parse_instruction_fails) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)GW_IDMAP_MODULE); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(fakeMessage)) + .SetReturn((const CONSTBUFFER *)&messageBuffer); + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn("write_once"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1) + .SetReturn("F000AA02-0451-4000-B000-000000000000"); + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "data")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Base64_Decoder(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((BUFFER_HANDLE)NULL); + + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_012: [ BLE_HL_Receive shall create a STRING_HANDLE from the characteristic_uuid data field. ] + //Tests_SRS_BLE_HL_17_013: [ If the string creation fails, BLE_HL_Receive shall return. ] + TEST_FUNCTION(BLE_HL_Receive_uuid_construction_fails) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)GW_IDMAP_MODULE); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(fakeMessage)) + .SetReturn((const CONSTBUFFER *)&messageBuffer); + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn("write_once"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1) + .SetReturn("F000AA02-0451-4000-B000-000000000000"); + STRICT_EXPECTED_CALL(mocks, STRING_construct(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((STRING_HANDLE)NULL); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_008: [ BLE_HL_Receive shall return if the JSON object does not contain the following fields: "type", "characteristic_uuid", and "data". ] + TEST_FUNCTION(BLE_HL_Receive_uuid_not_found) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)GW_IDMAP_MODULE); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(fakeMessage)) + .SetReturn((const CONSTBUFFER *)&messageBuffer); + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn("write_once"); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "characteristic_uuid")) + .IgnoreArgument(1) + .SetReturn((const char*)NULL); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_008: [ BLE_HL_Receive shall return if the JSON object does not contain the following fields: "type", "characteristic_uuid", and "data". ] + TEST_FUNCTION(BLE_HL_Receive_type_returns_null) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)GW_IDMAP_MODULE); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(fakeMessage)) + .SetReturn((const CONSTBUFFER *)&messageBuffer); + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*) NULL); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_007: [ If the message contents do not parse, then BLE_HL_Receive shall return. ] + TEST_FUNCTION(BLE_HL_Receive_json_value_get_object_fails) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)GW_IDMAP_MODULE); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(fakeMessage)) + .SetReturn((const CONSTBUFFER *)&messageBuffer); + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((JSON_Object*)NULL); + + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_007: [ If the message contents do not parse, then BLE_HL_Receive shall return. ] + TEST_FUNCTION(BLE_HL_Receive_json_parse_string_fails) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)GW_IDMAP_MODULE); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(fakeMessage)) + .SetReturn((const CONSTBUFFER *)&messageBuffer); + STRICT_EXPECTED_CALL(mocks, json_parse_string(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((JSON_Value*)NULL); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_007: [ If the message contents do not parse, then BLE_HL_Receive shall return. ] + TEST_FUNCTION(BLE_HL_Receive_message_no_content_fails) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)GW_IDMAP_MODULE); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(fakeMessage)) + .SetReturn((const CONSTBUFFER *)NULL); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_005: [ If the source of the message properties is not "mapping", then this function shall return. ] + TEST_FUNCTION(BLE_HL_Receive_message_source_not_mapping) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:CC:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"BLE"); + + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_003: [ If macAddress of the message property does not match this module's MAC address, then this function shall return. ] + TEST_FUNCTION(BLE_HL_Receive_message_wrong_mac_address) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1) + .SetReturn((const char *)"AA:BB:DD:DD:EE:FF"); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + //Tests_SRS_BLE_HL_17_002: [ If messageHandle properties does not contain "macAddress" property, then this function shall return. ] + //Tests_SRS_BLE_HL_17_004: [ If messageHandle properties does not contain "source" property, then this function shall return. ] + TEST_FUNCTION(BLE_HL_Receive_message_no_properties) + { + ///arrange + CBLEHLMocks mocks; + unsigned char fake = '\0'; + CONSTBUFFER messageBuffer; + messageBuffer.buffer = &fake; + messageBuffer.size = 1; + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + MESSAGE_HANDLE fakeMessage = (MESSAGE_HANDLE)0x42; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(fakeMessage)) + .SetFailReturn((CONSTMAP_HANDLE)NULL); + + ///act + BLE_HL_Receive(module, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + /*Tests_SRS_BLE_HL_13_016: [ BLE_HL_Receive shall do nothing if module is NULL. ]*/ + TEST_FUNCTION(BLE_HL_Receive_does_nothing_with_null_input1) + { + ///arrange + CBLEHLMocks mocks; + + ///act + BLE_HL_Receive(NULL, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + //Tests_SRS_BLE_HL_17_001: [ BLE_HL_Receive shall do nothing if message_handle is NULL. ] + TEST_FUNCTION(BLE_HL_Receive_does_nothing_with_null_input2) + { + ///arrange + CBLEHLMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "type")) + .IgnoreArgument(1) + .SetReturn((const char*)"write_at_init"); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + + auto module = BLE_HL_Create((MESSAGE_BUS_HANDLE)0x42, (const void*)FAKE_CONFIG); + mocks.ResetAllCalls(); + + ///act + BLE_HL_Receive(module, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLE_HL_Destroy(module); + } + + /*Tests_SRS_BLE_HL_13_019: [Module_GetAPIS shall return a non - NULL pointer to a structure of type MODULE_APIS that has all fields initialized to non - NULL values.]*/ + TEST_FUNCTION(Module_GetAPIS_returns_non_NULL) + { + ///arrage + CBLEHLMocks mocks; + + ///act + const MODULE_APIS* apis = Module_GetAPIS(); + + ///assert + ASSERT_IS_NOT_NULL(apis); + ASSERT_IS_NOT_NULL(apis->Module_Destroy); + ASSERT_IS_NOT_NULL(apis->Module_Create); + ASSERT_IS_NOT_NULL(apis->Module_Receive); + + ///cleanup + } + +END_TEST_SUITE(ble_hl_unittests) \ No newline at end of file diff --git a/modules/ble/tests/ble_hl_unittests/main.c b/modules/ble/tests/ble_hl_unittests/main.c new file mode 100644 index 00000000..0842a2fb --- /dev/null +++ b/modules/ble/tests/ble_hl_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include "testrunnerswitcher.h" +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(ble_hl_unittests, failedTestCount); + return failedTestCount; +} diff --git a/modules/ble/tests/ble_unittests/CMakeLists.txt b/modules/ble/tests/ble_unittests/CMakeLists.txt new file mode 100644 index 00000000..88805cdc --- /dev/null +++ b/modules/ble/tests/ble_unittests/CMakeLists.txt @@ -0,0 +1,63 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for ble_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName ble_unittests) + +if(LINUX) + set(${theseTestsName}_cpp_files + ${theseTestsName}_linux.cpp + ) + + # Include GIO headers/libs + include_directories(${GIOUNIX_INCLUDE_DIRS}) + set(LIBS ${GIOUNIX_LIBRARIES}) + + # BLE GATT I/O sources + set(ble_test_sources + ../../src/ble_utils.c + ../../src/ble.c + ) + set(ble_test_headers + ../../inc/gio_async_seq.h + ../../inc/ble_utils.h + ../../inc/ble_gatt_io_linux_common.h + ) +elseif(WIN32) + set(${theseTestsName}_cpp_files + ${theseTestsName}_windows.cpp + ) + + set(ble_test_sources + ../../src/ble.c + ../../src/ble_utils.c + ) +endif() + +set(ble_test_headers + ${ble_test_headers} + ../../inc/ble_gatt_io.h + ../../inc/ble_utils.h + ../../inc/ble.h +) + +include_directories( + ../../inc + ${GW_SRC} + ${GW_INC} +) + +set(${theseTestsName}_c_files + ${ble_test_sources} +) + +set(${theseTestsName}_h_files + ${ble_test_headers} +) + +add_definitions(-DGB_TIME_INTERCEPT -DNO_LOGGING) + +build_test_artifacts(${theseTestsName} ON ${LIBS}) diff --git a/modules/ble/tests/ble_unittests/ble_unittests_linux.cpp b/modules/ble/tests/ble_unittests/ble_unittests_linux.cpp new file mode 100644 index 00000000..22a37bcc --- /dev/null +++ b/modules/ble/tests/ble_unittests/ble_unittests_linux.cpp @@ -0,0 +1,3016 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "azure_c_shared_utility/macro_utils.h" + +/*the below is a horrible hack*/ +#include "macro_utils.h" +#undef DEFINE_ENUM +#define DEFINE_ENUM(enumName, ...) typedef enum C2(enumName, _TAG) { FOR_EACH_1(DEFINE_ENUMERATION_CONSTANT, __VA_ARGS__)} enumName; + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +#include +#include + +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/buffer_.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/threadapi.h" +#include "message.h" +#include "message_bus.h" +#include "ble_gatt_io.h" +#include "bleio_seq.h" +#include "messageproperties.h" +#include "ble.h" + +DEFINE_MICROMOCK_ENUM_TO_STRING(MAP_RESULT, MAP_RESULT_VALUES); + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +/*these are simple cached variables*/ +static pfModule_Create BLE_Create = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Destroy BLE_Destroy = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Receive BLE_Receive = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +#ifndef GB_TIME_INTERCEPT +#error these unit tests require the symbol GB_TIME_INTERCEPT to be defined +#else +extern "C" +{ + extern time_t gb_time(time_t *timer); + extern struct tm* gb_localtime(const time_t *timer); + extern size_t gb_strftime(char * s, size_t maxsize, const char * format, const struct tm * timeptr); +} +#endif + +#define TIME_IN_STRFTIME "time" + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit + +#include "vector.c" +#include "message.c" +#include "constbuffer.c" +#include "constmap.c" +#include "map.c" +#include "buffer.c" +#include "strings.c" +#include "crt_abstractions.c" +}; + +static bool g_call_on_read_complete = false; +static BLEIO_SEQ_RESULT g_read_result = BLEIO_SEQ_OK; + +static bool shouldThreadAPI_Create_invoke_callback = false; +static bool should_g_main_loop_quit_call_thread_func = false; +static THREAD_START_FUNC thread_start_func = NULL; +static void* thread_func_arg = NULL; + +class CBLEIOSequence +{ +public: + BLEIO_GATT_HANDLE _bleio_gatt_handle; + VECTOR_HANDLE _instructions; + ON_BLEIO_SEQ_READ_COMPLETE _on_read_complete; + ON_BLEIO_SEQ_WRITE_COMPLETE _on_write_complete; + +public: + CBLEIOSequence( + BLEIO_GATT_HANDLE bleio_gatt_handle, + VECTOR_HANDLE instructions, + ON_BLEIO_SEQ_READ_COMPLETE on_read_complete, + ON_BLEIO_SEQ_WRITE_COMPLETE on_write_complete + ) : + _bleio_gatt_handle(bleio_gatt_handle), + _instructions(instructions), + _on_read_complete(on_read_complete), + _on_write_complete(on_write_complete) + {} + + ~CBLEIOSequence() + { + if (_bleio_gatt_handle != NULL) + { + BLEIO_gatt_destroy(_bleio_gatt_handle); + } + + if (_instructions != NULL) + { + for (size_t i = 0, len = VECTOR_size(_instructions); i < len; i++) + { + BLEIO_SEQ_INSTRUCTION *instruction = (BLEIO_SEQ_INSTRUCTION *)VECTOR_element(_instructions, i); + if (instruction->characteristic_uuid != NULL) + { + STRING_delete(instruction->characteristic_uuid); + } + } + + VECTOR_destroy(_instructions); + } + } + + BLEIO_SEQ_RESULT run() + { + size_t len = VECTOR_size(_instructions); + if (g_call_on_read_complete && _on_read_complete != NULL && len > 0) + { + BLEIO_SEQ_INSTRUCTION* instr = (BLEIO_SEQ_INSTRUCTION*)VECTOR_element(_instructions, 0); + unsigned char fake_data[] = "data"; + size_t data_size = sizeof(fake_data) / sizeof(fake_data[0]); + + _on_read_complete( + (BLEIO_SEQ_HANDLE)this, + instr->context, + STRING_c_str(instr->characteristic_uuid), + instr->instruction_type, + g_read_result, + BUFFER_create(fake_data, data_size) + ); + } + + return g_read_result; + } + + BLEIO_SEQ_RESULT add_instruction(BLEIO_SEQ_INSTRUCTION* instruction) + { + return BLEIO_SEQ_OK; + } +}; + +TYPED_MOCK_CLASS(CBLEMocks, CGlobalMock) +{ +public: + // memory + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, size_t, VECTOR_size, VECTOR_HANDLE, vector) + size_t result2 = BASEIMPLEMENTATION::VECTOR_size(vector); + MOCK_METHOD_END(size_t, result2) + + MOCK_STATIC_METHOD_1(, void, VECTOR_destroy, VECTOR_HANDLE, vector) + BASEIMPLEMENTATION::VECTOR_destroy(vector); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, VECTOR_HANDLE, VECTOR_create, size_t, elementSize) + auto result2 = BASEIMPLEMENTATION::VECTOR_create(elementSize); + MOCK_METHOD_END(VECTOR_HANDLE, result2) + + MOCK_STATIC_METHOD_2(, void*, VECTOR_element, VECTOR_HANDLE, vector, size_t, index) + void* result2 = BASEIMPLEMENTATION::VECTOR_element(vector, index); + MOCK_METHOD_END(void*, result2) + + MOCK_STATIC_METHOD_3(, int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements) + auto result2 = BASEIMPLEMENTATION::VECTOR_push_back(handle, elements, numElements); + MOCK_METHOD_END(int, result2) + + MOCK_STATIC_METHOD_1(, void*, VECTOR_front, VECTOR_HANDLE, vector) + void* result2 = BASEIMPLEMENTATION::VECTOR_front(vector); + MOCK_METHOD_END(void*, result2) + + MOCK_STATIC_METHOD_3(, void, VECTOR_erase, VECTOR_HANDLE, handle, void*, elements, size_t, numElements) + BASEIMPLEMENTATION::VECTOR_erase(handle, elements, numElements); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, CONSTBUFFER_HANDLE, CONSTBUFFER_Create, const unsigned char*, source, size_t, size) + auto result2 = BASEIMPLEMENTATION::CONSTBUFFER_Create(source, size); + MOCK_METHOD_END(CONSTBUFFER_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, CONSTBUFFER_Destroy, CONSTBUFFER_HANDLE, constbufferHandle) + BASEIMPLEMENTATION::CONSTBUFFER_Destroy(constbufferHandle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, CONSTBUFFER_HANDLE, CONSTBUFFER_Clone, CONSTBUFFER_HANDLE, constbufferHandle) + CONSTBUFFER_HANDLE result2 = BASEIMPLEMENTATION::CONSTBUFFER_Clone(constbufferHandle); + MOCK_METHOD_END(CONSTBUFFER_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, CONSTBUFFER_HANDLE, CONSTBUFFER_CreateFromBuffer, BUFFER_HANDLE, buffer) + CONSTBUFFER_HANDLE result2 = BASEIMPLEMENTATION::CONSTBUFFER_CreateFromBuffer(buffer); + MOCK_METHOD_END(CONSTBUFFER_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, const CONSTBUFFER*, CONSTBUFFER_GetContent, CONSTBUFFER_HANDLE, constbufferHandle) + const CONSTBUFFER* result2 = BASEIMPLEMENTATION::CONSTBUFFER_GetContent(constbufferHandle); + MOCK_METHOD_END(const CONSTBUFFER*, result2) + + MOCK_STATIC_METHOD_1(, MESSAGE_HANDLE, Message_Create, const MESSAGE_CONFIG*, cfg) + MESSAGE_HANDLE result2 = BASEIMPLEMENTATION::Message_Create(cfg); + MOCK_METHOD_END(MESSAGE_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, MESSAGE_HANDLE, Message_CreateFromBuffer, const MESSAGE_BUFFER_CONFIG*, cfg) + MESSAGE_HANDLE result1 = BASEIMPLEMENTATION::Message_CreateFromBuffer(cfg); + MOCK_METHOD_END(MESSAGE_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, MESSAGE_HANDLE, Message_Clone, MESSAGE_HANDLE, message) + auto result2 = BASEIMPLEMENTATION::Message_Clone(message); + MOCK_METHOD_END(MESSAGE_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message) + CONSTMAP_HANDLE result1 = BASEIMPLEMENTATION::Message_GetProperties(message); + MOCK_METHOD_END(CONSTMAP_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, const CONSTBUFFER*, Message_GetContent, MESSAGE_HANDLE, message) + const CONSTBUFFER* result1 = BASEIMPLEMENTATION::Message_GetContent(message); + MOCK_METHOD_END(const CONSTBUFFER*, result1) + + MOCK_STATIC_METHOD_1(, CONSTBUFFER_HANDLE, Message_GetContentHandle, MESSAGE_HANDLE, message) + CONSTBUFFER_HANDLE result1 = BASEIMPLEMENTATION::Message_GetContentHandle(message); + MOCK_METHOD_END(CONSTBUFFER_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, void, Message_Destroy, MESSAGE_HANDLE, message) + BASEIMPLEMENTATION::Message_Destroy(message); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, BUFFER_HANDLE, BUFFER_create, const unsigned char*, source, size_t, size) + BUFFER_HANDLE result2 = BASEIMPLEMENTATION::BUFFER_create(source, size); + MOCK_METHOD_END(BUFFER_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, BUFFER_delete, BUFFER_HANDLE, handle) + BASEIMPLEMENTATION::BUFFER_delete(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, unsigned char*, BUFFER_u_char, BUFFER_HANDLE, handle) + auto result2 = BASEIMPLEMENTATION::BUFFER_u_char(handle); + MOCK_METHOD_END(unsigned char*, result2) + + MOCK_STATIC_METHOD_1(, size_t, BUFFER_length, BUFFER_HANDLE, handle) + auto result2 = BASEIMPLEMENTATION::BUFFER_length(handle); + MOCK_METHOD_END(size_t, result2) + + MOCK_STATIC_METHOD_1(, void, STRING_delete, STRING_HANDLE, handle) + BASEIMPLEMENTATION::STRING_delete(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, const char*, STRING_c_str, STRING_HANDLE, handle) + const char* result2 = BASEIMPLEMENTATION::STRING_c_str(handle); + MOCK_METHOD_END(const char*, result2) + + MOCK_STATIC_METHOD_1(, BLEIO_GATT_HANDLE, BLEIO_gatt_create, const BLE_DEVICE_CONFIG*, config) + auto result2 = (BLEIO_GATT_HANDLE)malloc(1); + MOCK_METHOD_END(BLEIO_GATT_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, BLEIO_gatt_destroy, BLEIO_GATT_HANDLE, bleio_gatt_handle) + free(bleio_gatt_handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, int, BLEIO_gatt_connect, BLEIO_GATT_HANDLE, bleio_gatt_handle, ON_BLEIO_GATT_CONNECT_COMPLETE, on_bleio_gatt_connect_complete, void*, callback_context) + on_bleio_gatt_connect_complete(bleio_gatt_handle, callback_context, BLEIO_GATT_CONNECT_OK); + int result2 = 0; + MOCK_METHOD_END(int, result2) + + MOCK_STATIC_METHOD_3(, void, BLEIO_gatt_disconnect, BLEIO_GATT_HANDLE, bleio_gatt_handle, ON_BLEIO_GATT_DISCONNECT_COMPLETE, on_bleio_gatt_disconnect_complete, void*, callback_context) + if (on_bleio_gatt_disconnect_complete != NULL) + { + on_bleio_gatt_disconnect_complete(bleio_gatt_handle, callback_context); + } + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, BLEIO_SEQ_HANDLE, BLEIO_Seq_Create, BLEIO_GATT_HANDLE, bleio_gatt_handle, VECTOR_HANDLE, instructions, ON_BLEIO_SEQ_READ_COMPLETE, on_read_complete, ON_BLEIO_SEQ_WRITE_COMPLETE, on_write_complete) + auto result2 = (BLEIO_SEQ_HANDLE)new CBLEIOSequence( + bleio_gatt_handle, + instructions, + on_read_complete, + on_write_complete + ); + MOCK_METHOD_END(BLEIO_SEQ_HANDLE, result2) + + MOCK_STATIC_METHOD_3(, void, BLEIO_Seq_Destroy, BLEIO_SEQ_HANDLE, bleio_seq_handle, ON_BLEIO_SEQ_DESTROY_COMPLETE, on_destroy_complete, void*, context) + if (on_destroy_complete != NULL) + { + on_destroy_complete(bleio_seq_handle, context); + } + delete (CBLEIOSequence*)bleio_seq_handle; + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, BLEIO_SEQ_RESULT, BLEIO_Seq_AddInstruction, BLEIO_SEQ_HANDLE, bleio_seq_handle, BLEIO_SEQ_INSTRUCTION*, instruction) + CBLEIOSequence* seq = (CBLEIOSequence*)bleio_seq_handle; + auto result2 = seq->add_instruction(instruction); + MOCK_METHOD_END(BLEIO_SEQ_RESULT, result2) + + MOCK_STATIC_METHOD_1(, BLEIO_SEQ_RESULT, BLEIO_Seq_Run, BLEIO_SEQ_HANDLE, bleio_seq_handle) + CBLEIOSequence* seq = (CBLEIOSequence*)bleio_seq_handle; + auto result2 = seq->run(); + MOCK_METHOD_END(BLEIO_SEQ_RESULT, result2) + + MOCK_STATIC_METHOD_2(, MESSAGE_BUS_RESULT, MessageBus_Publish, MESSAGE_BUS_HANDLE, bus, MESSAGE_HANDLE, message) + auto result2 = MESSAGE_BUS_OK; + MOCK_METHOD_END(MESSAGE_BUS_RESULT, result2) + + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, ConstMap_Create, MAP_HANDLE, sourceMap) + auto result2 = BASEIMPLEMENTATION::ConstMap_Create(sourceMap); + MOCK_METHOD_END(CONSTMAP_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, ConstMap_Destroy, CONSTMAP_HANDLE, handle) + BASEIMPLEMENTATION::ConstMap_Destroy(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, MAP_HANDLE, ConstMap_CloneWriteable, CONSTMAP_HANDLE, handle) + MAP_HANDLE result2 = BASEIMPLEMENTATION::ConstMap_CloneWriteable(handle); + MOCK_METHOD_END(MAP_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, ConstMap_Clone, CONSTMAP_HANDLE, handle) + auto result2 = BASEIMPLEMENTATION::ConstMap_Clone(handle); + MOCK_METHOD_END(CONSTMAP_HANDLE, result2) + + MOCK_STATIC_METHOD_2(, bool, ConstMap_ContainsKey, CONSTMAP_HANDLE, handle, const char*, key) + auto result2 = BASEIMPLEMENTATION::ConstMap_ContainsKey(handle, key); + MOCK_METHOD_END(bool, result2) + + MOCK_STATIC_METHOD_2(, bool, ConstMap_ContainsValue, CONSTMAP_HANDLE, handle, const char*, value) + auto result2 = BASEIMPLEMENTATION::ConstMap_ContainsValue(handle, value); + MOCK_METHOD_END(bool, result2) + + MOCK_STATIC_METHOD_2(, const char*, ConstMap_GetValue, CONSTMAP_HANDLE, handle, const char*, key) + auto result2 = BASEIMPLEMENTATION::ConstMap_GetValue(handle, key); + MOCK_METHOD_END(const char*, result2) + + MOCK_STATIC_METHOD_4(, CONSTMAP_RESULT, ConstMap_GetInternals, CONSTMAP_HANDLE, handle, const char*const**, keys, const char*const**, values, size_t*, count) + auto result2 = BASEIMPLEMENTATION::ConstMap_GetInternals(handle, keys, values, count); + MOCK_METHOD_END(CONSTMAP_RESULT, result2) + + MOCK_STATIC_METHOD_1(, MAP_HANDLE, Map_Clone, MAP_HANDLE, sourceMap) + auto result2 = BASEIMPLEMENTATION::Map_Clone(sourceMap); + MOCK_METHOD_END(MAP_HANDLE, result2); + + MOCK_STATIC_METHOD_1(, void, Map_Destroy, MAP_HANDLE, ptr) + BASEIMPLEMENTATION::Map_Destroy(ptr); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_3(, MAP_RESULT, Map_ContainsKey, MAP_HANDLE, handle, const char*, key, bool*, keyExists) + auto result2 = BASEIMPLEMENTATION::Map_ContainsKey(handle, key, keyExists); + MOCK_METHOD_END(MAP_RESULT, result2); + + MOCK_STATIC_METHOD_3(, MAP_RESULT, Map_ContainsValue, MAP_HANDLE, handle, const char*, value, bool*, valueExists) + auto result2 = BASEIMPLEMENTATION::Map_ContainsKey(handle, value, valueExists); + MOCK_METHOD_END(MAP_RESULT, result2); + + MOCK_STATIC_METHOD_2(, const char*, Map_GetValueFromKey, MAP_HANDLE, sourceMap, const char*, key) + auto result2 = BASEIMPLEMENTATION::Map_GetValueFromKey(sourceMap, key); + MOCK_METHOD_END(const char*, result2); + + MOCK_STATIC_METHOD_4(, MAP_RESULT, Map_GetInternals, MAP_HANDLE, handle, const char*const**, keys, const char*const**, values, size_t*, count) + auto result2 = BASEIMPLEMENTATION::Map_GetInternals(handle, keys, values, count); + MOCK_METHOD_END(MAP_RESULT, result2); + + MOCK_STATIC_METHOD_1(, MAP_HANDLE, Map_Create, MAP_FILTER_CALLBACK, mapFilterFunc) + auto result2 = BASEIMPLEMENTATION::Map_Create(mapFilterFunc); + MOCK_METHOD_END(MAP_HANDLE, result2) + + MOCK_STATIC_METHOD_3(, MAP_RESULT, Map_Add, MAP_HANDLE, handle, const char*, key, const char*, value) + auto result2 = BASEIMPLEMENTATION::Map_Add(handle, key, value); + MOCK_METHOD_END(MAP_RESULT, result2) + + MOCK_STATIC_METHOD_2(, int, mallocAndStrcpy_s, char**, destination, const char*, source) + auto result2 = BASEIMPLEMENTATION::mallocAndStrcpy_s(destination, source); + MOCK_METHOD_END(int, result2); + + MOCK_STATIC_METHOD_1(, STRING_HANDLE, STRING_construct, const char*, source) + auto result2 = BASEIMPLEMENTATION::STRING_construct(source); + MOCK_METHOD_END(STRING_HANDLE, result2) + + MOCK_STATIC_METHOD_2(, int, STRING_concat, STRING_HANDLE, s1, const char*, s2) + auto result2 = BASEIMPLEMENTATION::STRING_concat(s1, s2); + MOCK_METHOD_END(int, result2); + + MOCK_STATIC_METHOD_1(, STRING_HANDLE, STRING_new_JSON, const char*, source) + auto result2 = BASEIMPLEMENTATION::STRING_new_JSON(source); + MOCK_METHOD_END(STRING_HANDLE, result2) + + MOCK_STATIC_METHOD_2(, int, STRING_concat_with_STRING, STRING_HANDLE, s1, STRING_HANDLE, s2) + auto result2 = BASEIMPLEMENTATION::STRING_concat_with_STRING(s1, s2); + MOCK_METHOD_END(int, result2); + + MOCK_STATIC_METHOD_3(, THREADAPI_RESULT, ThreadAPI_Create, THREAD_HANDLE*, threadHandle, THREAD_START_FUNC, func, void*, arg) + THREADAPI_RESULT result2 = THREADAPI_OK; + thread_start_func = func; + thread_func_arg = arg; + if (shouldThreadAPI_Create_invoke_callback == true) + { + func(arg); + } + MOCK_METHOD_END(THREADAPI_RESULT, result2) + + MOCK_STATIC_METHOD_2(, THREADAPI_RESULT, ThreadAPI_Join, THREAD_HANDLE, threadHandle, int*, res) + THREADAPI_RESULT result2 = THREADAPI_OK; + MOCK_METHOD_END(THREADAPI_RESULT, result2) + + MOCK_STATIC_METHOD_1(, time_t, gb_time, time_t *, timer) + time_t result2 = (time_t)1; /*assume "1" is valid time_t*/ + MOCK_METHOD_END(time_t, result2); + + MOCK_STATIC_METHOD_1(, struct tm*, gb_localtime, const time_t *, timer) + struct tm* result2 = (struct tm*)0x42; + MOCK_METHOD_END(struct tm*, result2); + + MOCK_STATIC_METHOD_4(, size_t, gb_strftime, char*, s, size_t, maxsize, const char *, format, const struct tm *, timeptr) + if (maxsize < strlen(TIME_IN_STRFTIME) + 1) + { + ASSERT_FAIL("what is this puny message size!"); + } + else + { + strcpy(s, TIME_IN_STRFTIME); + } + MOCK_METHOD_END(size_t, maxsize); + + MOCK_STATIC_METHOD_0(, gint64, g_get_monotonic_time) + gint64 result2 = 1; + MOCK_METHOD_END(gint64, result2); + + MOCK_STATIC_METHOD_1(, void, g_usleep, gulong, microseconds) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, GMainLoop*, g_main_loop_new, GMainContext*, context, gboolean, is_running) + GMainLoop* result2 = (GMainLoop*)malloc(1); + MOCK_METHOD_END(GMainLoop*, result2); + + MOCK_STATIC_METHOD_1(, void, g_main_loop_unref, GMainLoop*, loop) + free(loop); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, void, g_main_loop_run, GMainLoop*, loop) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, void, g_main_loop_quit, GMainLoop*, loop) + if (should_g_main_loop_quit_call_thread_func && thread_start_func != NULL) + { + thread_start_func(thread_func_arg); + } + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, gboolean, g_main_loop_is_running, GMainLoop*, loop) + gboolean result2 = TRUE; + MOCK_METHOD_END(gboolean, result2); + + MOCK_STATIC_METHOD_1(, GMainContext*, g_main_loop_get_context, GMainLoop*, loop) + GMainContext* result2 = (GMainContext*)0x42; + MOCK_METHOD_END(GMainContext*, result2); + + MOCK_STATIC_METHOD_2(, gboolean, g_main_context_iteration, GMainContext*, context, gboolean, may_block) + gboolean result2 = TRUE; + MOCK_METHOD_END(gboolean, result2); +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, gballoc_free, void*, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , VECTOR_HANDLE, VECTOR_create, size_t, elementSize); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, VECTOR_destroy, VECTOR_HANDLE, vector); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , void*, VECTOR_element, VECTOR_HANDLE, vector, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , size_t, VECTOR_size, VECTOR_HANDLE, vector); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void*, VECTOR_front, VECTOR_HANDLE, vector); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , void, VECTOR_erase, VECTOR_HANDLE, handle, void*, elements, size_t, numElements); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , STRING_HANDLE, STRING_construct, const char*, s); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , int, STRING_concat, STRING_HANDLE, s1, const char*, s2); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , STRING_HANDLE, STRING_new_JSON, const char*, source); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , int, STRING_concat_with_STRING, STRING_HANDLE, s1, STRING_HANDLE, s2); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , const char*, STRING_c_str, STRING_HANDLE, handle); + +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , CONSTBUFFER_HANDLE, CONSTBUFFER_Create, const unsigned char*, source, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, CONSTBUFFER_Destroy, CONSTBUFFER_HANDLE, constbufferHandle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , CONSTBUFFER_HANDLE, CONSTBUFFER_Clone, CONSTBUFFER_HANDLE, constbufferHandle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , CONSTBUFFER_HANDLE, CONSTBUFFER_CreateFromBuffer, BUFFER_HANDLE, buffer); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , const CONSTBUFFER*, CONSTBUFFER_GetContent, CONSTBUFFER_HANDLE, constbufferHandle); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , CONSTMAP_HANDLE, ConstMap_Create, MAP_HANDLE, sourceMap); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, ConstMap_Destroy, CONSTMAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , MAP_HANDLE, ConstMap_CloneWriteable, CONSTMAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , CONSTMAP_HANDLE, ConstMap_Clone, CONSTMAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , bool, ConstMap_ContainsKey, CONSTMAP_HANDLE, handle, const char*, key); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , bool, ConstMap_ContainsValue, CONSTMAP_HANDLE, handle, const char*, value); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , const char*, ConstMap_GetValue, CONSTMAP_HANDLE, handle, const char*, key); +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEMocks, , CONSTMAP_RESULT, ConstMap_GetInternals, CONSTMAP_HANDLE, handle, const char*const**, keys, const char*const**, values, size_t*, count); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , MAP_HANDLE, Map_Create, MAP_FILTER_CALLBACK, mapFilterFunc); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , MAP_HANDLE, Map_Clone, MAP_HANDLE, sourceMap); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, Map_Destroy, MAP_HANDLE, ptr); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , MAP_RESULT, Map_ContainsKey, MAP_HANDLE, handle, const char*, key, bool*, keyExists); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , MAP_RESULT, Map_ContainsValue, MAP_HANDLE, handle, const char*, key, bool*, keyExists); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , const char*, Map_GetValueFromKey, MAP_HANDLE, ptr, const char*, key); +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEMocks, , MAP_RESULT, Map_GetInternals, MAP_HANDLE, handle, const char*const**, keys, const char*const**, values, size_t*, count); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , MAP_RESULT, Map_Add, MAP_HANDLE, handle, const char*, key, const char*, value); + +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , int, mallocAndStrcpy_s, char**, destination, const char*, source) + +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , THREADAPI_RESULT, ThreadAPI_Create, THREAD_HANDLE*, threadHandle, THREAD_START_FUNC, func, void*, arg); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , THREADAPI_RESULT, ThreadAPI_Join, THREAD_HANDLE, threadHandle, int*, res); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , MESSAGE_HANDLE, Message_Create, const MESSAGE_CONFIG*, cfg); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , MESSAGE_HANDLE, Message_CreateFromBuffer, const MESSAGE_BUFFER_CONFIG*, cfg); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , MESSAGE_HANDLE, Message_Clone, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , const CONSTBUFFER*, Message_GetContent, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , CONSTBUFFER_HANDLE, Message_GetContentHandle, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, Message_Destroy, MESSAGE_HANDLE, message); + +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , BUFFER_HANDLE, BUFFER_create, const unsigned char*, source, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, BUFFER_delete, BUFFER_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , unsigned char*, BUFFER_u_char, BUFFER_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , size_t, BUFFER_length, BUFFER_HANDLE, handle); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, STRING_delete, STRING_HANDLE, handle); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , BLEIO_GATT_HANDLE, BLEIO_gatt_create, const BLE_DEVICE_CONFIG*, config); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, BLEIO_gatt_destroy, BLEIO_GATT_HANDLE, bleio_gatt_handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , int, BLEIO_gatt_connect, BLEIO_GATT_HANDLE, bleio_gatt_handle, ON_BLEIO_GATT_CONNECT_COMPLETE, on_bleio_gatt_connect_complete, void*, callback_context); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , void, BLEIO_gatt_disconnect, BLEIO_GATT_HANDLE, bleio_gatt_handle, ON_BLEIO_GATT_DISCONNECT_COMPLETE, on_bleio_gatt_disconnect_complete, void*, callback_context); + +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEMocks, , BLEIO_SEQ_HANDLE, BLEIO_Seq_Create, BLEIO_GATT_HANDLE, bleio_gatt_handle, VECTOR_HANDLE, instructions, ON_BLEIO_SEQ_READ_COMPLETE, on_read_complete, ON_BLEIO_SEQ_WRITE_COMPLETE, on_write_complete); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , void, BLEIO_Seq_Destroy, BLEIO_SEQ_HANDLE, bleio_seq_handle, ON_BLEIO_SEQ_DESTROY_COMPLETE, on_destroy_complete, void*, context); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , BLEIO_SEQ_RESULT, BLEIO_Seq_Run, BLEIO_SEQ_HANDLE, bleio_seq_handle); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , BLEIO_SEQ_RESULT, BLEIO_Seq_AddInstruction, BLEIO_SEQ_HANDLE, bleio_seq_handle, BLEIO_SEQ_INSTRUCTION*, instruction); + +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , MESSAGE_BUS_RESULT, MessageBus_Publish, MESSAGE_BUS_HANDLE, bus, MESSAGE_HANDLE, message); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , time_t, gb_time, time_t*, timer); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , struct tm*, gb_localtime, const time_t*, timer); +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEMocks, , size_t, gb_strftime, char*, s, size_t, maxsize, const char *, format, const struct tm *, timeptr); + +DECLARE_GLOBAL_MOCK_METHOD_0(CBLEMocks, , gint64, g_get_monotonic_time); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, g_usleep, gulong, microseconds); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , GMainContext*, g_main_loop_get_context, GMainLoop*, loop); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , gboolean, g_main_context_iteration, GMainContext*, context, gboolean, may_block); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , GMainLoop*, g_main_loop_new, GMainContext*, context, gboolean, is_running); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, g_main_loop_unref, GMainLoop*, loop); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, g_main_loop_run, GMainLoop*, loop); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , gboolean, g_main_loop_is_running, GMainLoop*, loop); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, g_main_loop_quit, GMainLoop*, loop); + +BEGIN_TEST_SUITE(ble_unittests) + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + + BLE_Create = Module_GetAPIS()->Module_Create; + BLE_Destroy = Module_GetAPIS()->Module_Destroy; + BLE_Receive = Module_GetAPIS()->Module_Receive; + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + + g_call_on_read_complete = false; + g_read_result = BLEIO_SEQ_OK; + shouldThreadAPI_Create_invoke_callback = false; + thread_start_func = NULL; + should_g_main_loop_quit_call_thread_func = false; + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + } + + /*Tests_SRS_BLE_13_001: [ BLE_Create shall return NULL if bus is NULL . ]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_bus_is_NULL) + { + ///arrange + CBLEMocks mocks; + + ///act + auto result = BLE_Create(NULL, (const void*)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_13_002: [ BLE_Create shall return NULL if configuration is NULL . ]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_config_is_NULL) + { + ///arrange + CBLEMocks mocks; + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_13_003: [ BLE_Create shall return NULL if configuration->instructions is NULL . ]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_config_instructions_is_NULL) + { + ///arrange + CBLEMocks mocks; + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + NULL + }; + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLE_13_004: [BLE_Create shall return NULL if the configuration->instructions vector is empty(size is zero).]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_config_instructions_is_empty) + { + ///arrange + CBLEMocks mocks; + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + VECTOR_create(sizeof(BLE_INSTRUCTION)) + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(config.instructions); + } + + /*Tests_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_malloc_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + STRING_delete(instr1.characteristic_uuid); + VECTOR_destroy(config.instructions); + } + + /*Tests_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_BLEIO_gatt_create_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))) + .SetFailReturn((BLEIO_GATT_HANDLE)NULL); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + STRING_delete(instr1.characteristic_uuid); + VECTOR_destroy(config.instructions); + } + + /*Tests_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_VECTOR_create_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))) + .SetFailReturn((VECTOR_HANDLE)NULL); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + STRING_delete(instr1.characteristic_uuid); + VECTOR_destroy(config.instructions); + } + + /*Tests_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_VECTOR_push_back_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn((int)1); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + STRING_delete(instr1.characteristic_uuid); + VECTOR_destroy(config.instructions); + } + + /*Tests_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_BLEIO_Seq_Create_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetFailReturn((BLEIO_SEQ_HANDLE)NULL); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + STRING_delete(instr1.characteristic_uuid); + VECTOR_destroy(config.instructions); + } + + /*Tests_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_g_main_loop_new_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Destroy(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) // in ~CBLEIOSequence() + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) // in ~CBLEIOSequence() + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) // in ~CBLEIOSequence() + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, STRING_delete(instr1.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)) + .SetFailReturn((GMainLoop*)NULL); + + should_g_main_loop_quit_call_thread_func = true; + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(config.instructions); + } + + /*Tests_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_ThreadAPI_Create_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Destroy(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) // in ~CBLEIOSequence() + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) // in ~CBLEIOSequence() + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) // in ~CBLEIOSequence() + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetFailReturn((THREADAPI_RESULT)THREADAPI_ERROR); + + STRICT_EXPECTED_CALL(mocks, STRING_delete(instr1.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, g_main_loop_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + should_g_main_loop_quit_call_thread_func = true; + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(config.instructions); + } + + /*Tests_SRS_BLE_13_005: [ BLE_Create shall return NULL if an underlying API call fails. ]*/ + /*Tests_SRS_BLE_13_012: [ BLE_Create shall return NULL if BLEIO_gatt_connect returns a non-zero value. ]*/ + TEST_FUNCTION(BLE_Create_returns_NULL_when_BLEIO_gatt_connect_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Destroy(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetFailReturn((int)1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) // in ~CBLEIOSequence() + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) // in ~CBLEIOSequence() + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) // in ~CBLEIOSequence() + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, STRING_delete(instr1.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, g_get_monotonic_time()); + STRICT_EXPECTED_CALL(mocks, g_get_monotonic_time()); + STRICT_EXPECTED_CALL(mocks, g_main_loop_get_context(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_main_loop_run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_main_loop_is_running(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_main_loop_is_running(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_main_loop_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_main_loop_quit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + should_g_main_loop_quit_call_thread_func = true; + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(config.instructions); + } + + /*Tests_SRS_BLE_13_006: [ BLE_Create shall return a non-NULL MODULE_HANDLE when successful. ]*/ + /*Tests_SRS_BLE_13_009: [ BLE_Create shall allocate memory for an instance of the BLE_HANDLE_DATA structure and use that as the backing structure for the module handle. ]*/ + /*Tests_SRS_BLE_13_008: [ BLE_Create shall create and initialize the bleio_gatt field in the BLE_HANDLE_DATA object by calling BLEIO_gatt_create. ]*/ + /*Tests_SRS_BLE_13_010: [ BLE_Create shall create and initialize the bleio_seq field in the BLE_HANDLE_DATA object by calling BLEIO_Seq_Create. ]*/ + /*Tests_SRS_BLE_13_011: [ BLE_Create shall asynchronously open a connection to the BLE device by calling BLEIO_gatt_connect. ]*/ + /*Tests_SRS_BLE_13_014: [ If the asynchronous call to BLEIO_gatt_connect is successful then the BLEIO_Seq_Run function shall be called on the bleio_seq field from BLE_HANDLE_DATA. ]*/ + TEST_FUNCTION(BLE_Create_succeeds) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) // CBLEIOSequence::run + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(on_read_complete_does_not_publish_message_when_read_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_ERROR; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(on_read_complete_does_not_publish_message_when_Map_Create_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Map_Create(NULL)) + .SetFailReturn((MAP_HANDLE)NULL); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(on_read_complete_does_not_publish_message_when_time_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Map_Create(NULL)); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)) + .SetFailReturn((time_t)-1); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(on_read_complete_does_not_publish_message_when_localtime_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Map_Create(NULL)); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((struct tm*)NULL); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(on_read_complete_does_not_publish_message_when_strftime_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Map_Create(NULL)); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetFailReturn((size_t)0); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(on_read_complete_does_not_publish_message_when_first_Map_Add_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Map_Create(NULL)); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_BLE_CONTROLLER_INDEX_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .SetFailReturn((MAP_RESULT)MAP_ERROR); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(on_read_complete_does_not_publish_message_when_second_Map_Add_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Map_Create(NULL)); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_BLE_CONTROLLER_INDEX_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .SetFailReturn((MAP_RESULT)MAP_ERROR); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(on_read_complete_does_not_publish_message_when_third_Map_Add_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Map_Create(NULL)); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_BLE_CONTROLLER_INDEX_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_TIMESTAMP_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .SetFailReturn((MAP_RESULT)MAP_ERROR); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(on_read_complete_does_not_publish_message_when_fourth_Map_Add_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Map_Create(NULL)); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_BLE_CONTROLLER_INDEX_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_TIMESTAMP_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_CHARACTERISTIC_UUID_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3) + .SetFailReturn((MAP_RESULT)MAP_ERROR); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(on_read_complete_does_not_publish_message_when_fifth_Map_Add_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Map_Create(NULL)); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_BLE_CONTROLLER_INDEX_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_TIMESTAMP_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_CHARACTERISTIC_UUID_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_TELEMETRY)) + .IgnoreArgument(1) + .SetFailReturn((MAP_RESULT)MAP_ERROR); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(on_read_complete_does_not_publish_message_when_Message_Create_fails) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_length(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_read_complete + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_read_complete + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Map_Create(NULL)); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_BLE_CONTROLLER_INDEX_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_TIMESTAMP_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_CHARACTERISTIC_UUID_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_TELEMETRY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + // mallocAndStrcpy_s is called twice for each property and we have 5 properties + for (size_t i = 0; i < (5 * 2); i++) + { + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + } + + STRICT_EXPECTED_CALL(mocks, Message_Create(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((MESSAGE_HANDLE)NULL); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + /*Tests_SRS_BLE_13_019: BLE_Create shall handle the ON_BLEIO_SEQ_READ_COMPLETE callback on the BLE I/O sequence. If the call is successful then a new message shall be published on the message bus with the buffer that was read as the content of the message along with the following properties: + >| Property Name | Description | + >|-------------------------|---------------------------------------------------------------| + >| ble_controller_index | The index of the bluetooth radio hardware on the device. | + >| mac_address | MAC address of the BLE device from which the data was read. | + >| timestamp | Timestamp indicating when the data was read. | + ]*/ + TEST_FUNCTION(on_read_complete_publishes_message) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_create(&(config.device_config))); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_connect(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, BUFFER_length(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_read_complete + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // on_read_complete + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); // on_read_complete + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION))); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(config.instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // CBLEIOSequence::run + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, Map_Create(NULL)); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_BLE_CONTROLLER_INDEX_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_TIMESTAMP_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_CHARACTERISTIC_UUID_PROPERTY, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, Map_Add(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_TELEMETRY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + // mallocAndStrcpy_s is called twice for each property and we have 5 properties + // and we do the whole thing twice + for (size_t i = 0; i < (5 * 2) * 2; i++) + { + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + } + + STRICT_EXPECTED_CALL(mocks, Message_Create(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, MessageBus_Publish((MESSAGE_BUS_HANDLE)0x42, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, g_main_loop_new(NULL, FALSE)); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Create(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments() + .SetReturn((THREADAPI_RESULT)THREADAPI_OK); + + ///act + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(result); + VECTOR_destroy(instructions); + } + + /*Tests_SRS_BLE_13_016: [ If module is NULL BLE_Destroy shall do nothing. ]*/ + TEST_FUNCTION(BLE_Destroy_does_nothing_with_NULL_input) + { + ///arrange + CBLEMocks mocks; + + ///act + BLE_Destroy(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLE_13_017: [ BLE_Destroy shall free all resources. ]*/ + TEST_FUNCTION(BLE_Destroy_happy_path) + { + ///arrange + CBLEMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // have the on_read_complete callback called + g_read_result = BLEIO_SEQ_OK; + g_call_on_read_complete = true; + + // we don't want ThreadAPI_Create to call the callback + shouldThreadAPI_Create_invoke_callback = false; + + // we want thread func called from g_main_loop_quit + should_g_main_loop_quit_call_thread_func = true; + + auto result = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_disconnect(IGNORED_PTR_ARG, NULL, NULL)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_Destroy(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_get_monotonic_time()); + STRICT_EXPECTED_CALL(mocks, g_main_loop_get_context(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_main_loop_run(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_main_loop_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_main_loop_quit(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ThreadAPI_Join(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + ///act + BLE_Destroy(result); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + VECTOR_destroy(instructions); + } + + /*Tests_SRS_BLE_13_007: [ Module_GetAPIS shall return a non-NULL pointer to a structure of type MODULE_APIS that has all fields initialized to non-NULL values. ]*/ + TEST_FUNCTION(Module_GetAPIS_returns_non_NULL_and_non_NULL_fields) + { + ///arrrange + + ///act + auto result = Module_GetAPIS(); + + ///assert + ASSERT_IS_NOT_NULL(result); + ASSERT_IS_NOT_NULL(result->Module_Create); + ASSERT_IS_NOT_NULL(result->Module_Destroy); + ASSERT_IS_NOT_NULL(result->Module_Receive); + } + + /*Tests_SRS_BLE_13_018: [ BLE_Receive shall do nothing if module is NULL or if message is NULL. ]*/ + TEST_FUNCTION(BLE_Receive_does_nothing_when_module_handle_is_NULL) + { + ///arrrange + CBLEMocks mocks; + + ///act + BLE_Receive(NULL, (MESSAGE_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + } + + /*Tests_SRS_BLE_13_018: [ BLE_Receive shall do nothing if module is NULL or if message is NULL. ]*/ + TEST_FUNCTION(BLE_Receive_does_nothing_when_message_is_NULL) + { + ///arrrange + CBLEMocks mocks; + + ///act + BLE_Receive((MODULE_HANDLE)0x42, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + } + + /*Tests_SRS_BLE_13_020: [ BLE_Receive shall ignore all messages except those that have the following properties: + >| Property Name | Description | + >|-------------------------|-------------------------------------------------------------------------| + >| source | This property should have the value "BLE". | + >| macAddress | MAC address of the BLE device to which the data to should be written. | + ]*/ + TEST_FUNCTION(BLE_Receive_does_nothing_when_message_does_not_contain_source_property) + { + ///arrrange + CBLEMocks mocks; + + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // we don't want ThreadAPI_Create to call the callback + shouldThreadAPI_Create_invoke_callback = false; + + auto handle = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + MAP_HANDLE properties = Map_Create(NULL); // empty map + MESSAGE_CONFIG message_config = + { + 0, NULL, + properties + }; + + MESSAGE_HANDLE message = Message_Create(&message_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(message)); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + + ///act + BLE_Receive(handle, message); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(handle); + Map_Destroy(properties); + Message_Destroy(message); + VECTOR_destroy(instructions); + } + + /*Tests_SRS_BLE_13_020: [ BLE_Receive shall ignore all messages except those that have the following properties: + >| Property Name | Description | + >|-------------------------|-------------------------------------------------------------------------| + >| source | This property should have the value "BLE". | + >| macAddress | MAC address of the BLE device to which the data to should be written. | + ]*/ + TEST_FUNCTION(BLE_Receive_does_nothing_when_message_contains_source_property_with_invalid_module) + { + ///arrrange + CBLEMocks mocks; + + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // we don't want ThreadAPI_Create to call the callback + shouldThreadAPI_Create_invoke_callback = false; + + auto handle = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + MAP_HANDLE properties = Map_Create(NULL); + Map_Add(properties, GW_SOURCE_PROPERTY, "boo"); + MESSAGE_CONFIG message_config = + { + 0, NULL, + properties + }; + + MESSAGE_HANDLE message = Message_Create(&message_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(message)); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + + ///act + BLE_Receive(handle, message); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(handle); + Map_Destroy(properties); + Message_Destroy(message); + VECTOR_destroy(instructions); + } + + /*Tests_SRS_BLE_13_020: [ BLE_Receive shall ignore all messages except those that have the following properties: + >| Property Name | Description | + >|-------------------------|-------------------------------------------------------------------------| + >| source | This property should have the value "BLE". | + >| macAddress | MAC address of the BLE device to which the data to should be written. | + ]*/ + TEST_FUNCTION(BLE_Receive_does_nothing_when_message_does_not_contain_mac_address) + { + ///arrrange + CBLEMocks mocks; + + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // we don't want ThreadAPI_Create to call the callback + shouldThreadAPI_Create_invoke_callback = false; + + auto handle = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + MAP_HANDLE properties = Map_Create(NULL); + Map_Add(properties, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_COMMAND); + MESSAGE_CONFIG message_config = + { + 0, NULL, + properties + }; + + MESSAGE_HANDLE message = Message_Create(&message_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(message)); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + + ///act + BLE_Receive(handle, message); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(handle); + Map_Destroy(properties); + Message_Destroy(message); + VECTOR_destroy(instructions); + } + + /*Tests_SRS_BLE_13_020: [ BLE_Receive shall ignore all messages except those that have the following properties: + >| Property Name | Description | + >|-------------------------|-------------------------------------------------------------------------| + >| source | This property should have the value "BLE". | + >| macAddress | MAC address of the BLE device to which the data to should be written. | + ]*/ + TEST_FUNCTION(BLE_Receive_does_nothing_when_message_contains_invalid_mac_address) + { + ///arrrange + CBLEMocks mocks; + + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // we don't want ThreadAPI_Create to call the callback + shouldThreadAPI_Create_invoke_callback = false; + + auto handle = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + MAP_HANDLE properties = Map_Create(NULL); + Map_Add(properties, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_COMMAND); + Map_Add(properties, GW_MAC_ADDRESS_PROPERTY, "boo"); + + MESSAGE_CONFIG message_config = + { + 0, NULL, + properties + }; + + MESSAGE_HANDLE message = Message_Create(&message_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(message)); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + + ///act + BLE_Receive(handle, message); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(handle); + Map_Destroy(properties); + Message_Destroy(message); + VECTOR_destroy(instructions); + } + + /*Tests_SRS_BLE_13_022: [ BLE_Receive shall ignore the message unless the 'macAddress' property matches the MAC address that was passed to this module when it was created. ]*/ + TEST_FUNCTION(BLE_Receive_does_nothing_when_message_contains_foreign_mac_address) + { + ///arrrange + CBLEMocks mocks; + + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // we don't want ThreadAPI_Create to call the callback + shouldThreadAPI_Create_invoke_callback = false; + + auto handle = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + MAP_HANDLE properties = Map_Create(NULL); + Map_Add(properties, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_COMMAND); + Map_Add(properties, GW_MAC_ADDRESS_PROPERTY, "FF:EE:DD:CC:BB:AA"); + + MESSAGE_CONFIG message_config = + { + 0, NULL, + properties + }; + + MESSAGE_HANDLE message = Message_Create(&message_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(message)); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + + ///act + BLE_Receive(handle, message); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(handle); + Map_Destroy(properties); + Message_Destroy(message); + VECTOR_destroy(instructions); + } + + TEST_FUNCTION(BLE_Receive_does_nothing_when_message_does_not_have_content) + { + ///arrrange + CBLEMocks mocks; + + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // we don't want ThreadAPI_Create to call the callback + shouldThreadAPI_Create_invoke_callback = false; + + auto handle = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + MAP_HANDLE properties = Map_Create(NULL); + Map_Add(properties, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_COMMAND); + Map_Add(properties, GW_MAC_ADDRESS_PROPERTY, "AA:BB:CC:DD:EE:FF"); + + MESSAGE_CONFIG message_config = + { + 0, NULL, // no content + properties + }; + + MESSAGE_HANDLE message = Message_Create(&message_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(message)); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(message)); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_GetContent(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + + ///act + BLE_Receive(handle, message); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(handle); + Map_Destroy(properties); + Message_Destroy(message); + VECTOR_destroy(instructions); + } + + /*Tests_SRS_BLE_13_021: [ BLE_Receive shall treat the content of the message as a BLE_INSTRUCTION and schedule it for execution by calling BLEIO_Seq_AddInstruction. ]*/ + TEST_FUNCTION(BLE_Receive_calls_BLEIO_Seq_AddInstruction) + { + ///arrrange + CBLEMocks mocks; + + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLE_INSTRUCTION)); + BLE_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + BLE_CONFIG config = + { + { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, + instructions + }; + + // we don't want ThreadAPI_Create to call the callback + shouldThreadAPI_Create_invoke_callback = false; + + auto handle = BLE_Create((MESSAGE_BUS_HANDLE)0x42, &config); + + MAP_HANDLE properties = Map_Create(NULL); + Map_Add(properties, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_COMMAND); + Map_Add(properties, GW_MAC_ADDRESS_PROPERTY, "AA:BB:CC:DD:EE:FF"); + + BLE_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + MESSAGE_CONFIG message_config = + { + sizeof(BLE_INSTRUCTION), + (const unsigned char*)&instruction, + properties + }; + + MESSAGE_HANDLE message = Message_Create(&message_config); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(message)); + STRICT_EXPECTED_CALL(mocks, Message_GetContent(message)); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Clone(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_GetContent(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_GetValueFromKey(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_Seq_AddInstruction(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + ///act + BLE_Receive(handle, message); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + should_g_main_loop_quit_call_thread_func = true; + BLE_Destroy(handle); + Map_Destroy(properties); + Message_Destroy(message); + VECTOR_destroy(instructions); + BUFFER_delete(instruction.data.buffer); + STRING_delete(instruction.characteristic_uuid); + } + +END_TEST_SUITE(ble_unittests) diff --git a/modules/ble/tests/ble_unittests/ble_unittests_windows.cpp b/modules/ble/tests/ble_unittests/ble_unittests_windows.cpp new file mode 100644 index 00000000..587ccd92 --- /dev/null +++ b/modules/ble/tests/ble_unittests/ble_unittests_windows.cpp @@ -0,0 +1,261 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +#include "azure_c_shared_utility/lock.h" +#include "bleio_seq.h" + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +#ifndef GB_TIME_INTERCEPT +#error these unit tests require the symbol GB_TIME_INTERCEPT to be defined +#else +extern "C" +{ + extern time_t gb_time(time_t *timer); + extern struct tm* gb_localtime(const time_t *timer); + extern size_t gb_strftime(char * s, size_t maxsize, const char * format, const struct tm * timeptr); +} +#endif + +#define TIME_IN_STRFTIME "time" + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit +}; + +static bool g_call_on_read_complete = false; +static BLEIO_SEQ_RESULT g_read_result = BLEIO_SEQ_OK; + +class CBLEIOSequence +{ +public: + BLEIO_GATT_HANDLE _bleio_gatt_handle; + VECTOR_HANDLE _instructions; + ON_BLEIO_SEQ_READ_COMPLETE _on_read_complete; + ON_BLEIO_SEQ_WRITE_COMPLETE _on_write_complete; + +public: + CBLEIOSequence( + BLEIO_GATT_HANDLE bleio_gatt_handle, + VECTOR_HANDLE instructions, + ON_BLEIO_SEQ_READ_COMPLETE on_read_complete, + ON_BLEIO_SEQ_WRITE_COMPLETE on_write_complete + ) : + _bleio_gatt_handle(bleio_gatt_handle), + _instructions(instructions), + _on_read_complete(on_read_complete), + _on_write_complete(on_write_complete) + {} + + ~CBLEIOSequence() + { + if (_bleio_gatt_handle != NULL) + { + BLEIO_gatt_destroy(_bleio_gatt_handle); + } + + if (_instructions != NULL) + { + for (size_t i = 0, len = VECTOR_size(_instructions); i < len; i++) + { + BLEIO_SEQ_INSTRUCTION *instruction = (BLEIO_SEQ_INSTRUCTION *)VECTOR_element(_instructions, i); + if (instruction->characteristic_uuid != NULL) + { + STRING_delete(instruction->characteristic_uuid); + } + } + + VECTOR_destroy(_instructions); + } + } + + BLEIO_SEQ_RESULT run() + { + size_t len = VECTOR_size(_instructions); + if (g_call_on_read_complete && _on_read_complete != NULL && len > 0) + { + BLEIO_SEQ_INSTRUCTION* instr = (BLEIO_SEQ_INSTRUCTION*)VECTOR_element(_instructions, 0); + unsigned char fake_data[] = "data"; + size_t data_size = sizeof(fake_data) / sizeof(fake_data[0]); + + _on_read_complete( + (BLEIO_SEQ_HANDLE)this, + instr->context, + STRING_c_str(instr->characteristic_uuid), + instr->instruction_type, + g_read_result, + BUFFER_create(fake_data, data_size) + ); + } + + return g_read_result; + } + + BLEIO_SEQ_RESULT add_instruction(BLEIO_SEQ_INSTRUCTION* instruction) + { + return BLEIO_SEQ_OK; + } +}; + +TYPED_MOCK_CLASS(CBLEMocks, CGlobalMock) +{ +public: + // memory + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_5(, void, on_write_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context, const char*, characteristic_uuid, BLEIO_SEQ_INSTRUCTION_TYPE, type, BLEIO_SEQ_RESULT, result2) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_6(, void, on_read_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context, const char*, characteristic_uuid, BLEIO_SEQ_INSTRUCTION_TYPE, type, BLEIO_SEQ_RESULT, result2, BUFFER_HANDLE, data) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, time_t, gb_time, time_t *, timer) + time_t result2 = (time_t)1; /*assume "1" is valid time_t*/ + MOCK_METHOD_END(time_t, result2); + + MOCK_STATIC_METHOD_1(, struct tm*, gb_localtime, const time_t *, timer) + struct tm* result2 = (struct tm*)0x42; + MOCK_METHOD_END(struct tm*, result2); + + MOCK_STATIC_METHOD_4(, size_t, gb_strftime, char*, s, size_t, maxsize, const char *, format, const struct tm *, timeptr) + if (maxsize < strlen(TIME_IN_STRFTIME) + 1) + { + ASSERT_FAIL("what is this puny message size!"); + } + else + { + strcpy(s, TIME_IN_STRFTIME); + } + MOCK_METHOD_END(size_t, maxsize); + + MOCK_STATIC_METHOD_1(, BLEIO_GATT_HANDLE, BLEIO_gatt_create, const BLE_DEVICE_CONFIG*, config) + auto result2 = (BLEIO_GATT_HANDLE)malloc(1); + MOCK_METHOD_END(BLEIO_GATT_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, BLEIO_gatt_destroy, BLEIO_GATT_HANDLE, bleio_gatt_handle) + free(bleio_gatt_handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, int, BLEIO_gatt_connect, BLEIO_GATT_HANDLE, bleio_gatt_handle, ON_BLEIO_GATT_CONNECT_COMPLETE, on_bleio_gatt_connect_complete, void*, callback_context) + on_bleio_gatt_connect_complete(bleio_gatt_handle, callback_context, BLEIO_GATT_CONNECT_OK); + int result2 = 0; + MOCK_METHOD_END(int, result2) + + MOCK_STATIC_METHOD_3(, void, BLEIO_gatt_disconnect, BLEIO_GATT_HANDLE, bleio_gatt_handle, ON_BLEIO_GATT_DISCONNECT_COMPLETE, on_bleio_gatt_disconnect_complete, void*, callback_context) + if (on_bleio_gatt_disconnect_complete != NULL) + { + on_bleio_gatt_disconnect_complete(bleio_gatt_handle, callback_context); + } + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, BLEIO_SEQ_HANDLE, BLEIO_Seq_Create, BLEIO_GATT_HANDLE, bleio_gatt_handle, VECTOR_HANDLE, instructions, ON_BLEIO_SEQ_READ_COMPLETE, on_read_complete, ON_BLEIO_SEQ_WRITE_COMPLETE, on_write_complete) + auto result2 = (BLEIO_SEQ_HANDLE)new CBLEIOSequence( + bleio_gatt_handle, + instructions, + on_read_complete, + on_write_complete + ); + MOCK_METHOD_END(BLEIO_SEQ_HANDLE, result2) + + MOCK_STATIC_METHOD_2(, BLEIO_SEQ_RESULT, BLEIO_Seq_AddInstruction, BLEIO_SEQ_HANDLE, bleio_seq_handle, BLEIO_SEQ_INSTRUCTION*, instruction) + CBLEIOSequence* seq = (CBLEIOSequence*)bleio_seq_handle; + auto result2 = seq->add_instruction(instruction); + MOCK_METHOD_END(BLEIO_SEQ_RESULT, result2) + + MOCK_STATIC_METHOD_3(, void, BLEIO_Seq_Destroy, BLEIO_SEQ_HANDLE, bleio_seq_handle, ON_BLEIO_SEQ_DESTROY_COMPLETE, on_destroy_complete, void*, context) + if (on_destroy_complete != NULL) + { + on_destroy_complete(bleio_seq_handle, context); + } + delete (CBLEIOSequence*)bleio_seq_handle; + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, BLEIO_SEQ_RESULT, BLEIO_Seq_Run, BLEIO_SEQ_HANDLE, bleio_seq_handle) + CBLEIOSequence* seq = (CBLEIOSequence*)bleio_seq_handle; + auto result2 = seq->run(); + MOCK_METHOD_END(BLEIO_SEQ_RESULT, result2) +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, gballoc_free, void*, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , time_t, gb_time, time_t*, timer); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , struct tm*, gb_localtime, const time_t*, timer); +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEMocks, , size_t, gb_strftime, char*, s, size_t, maxsize, const char *, format, const struct tm *, timeptr); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , BLEIO_GATT_HANDLE, BLEIO_gatt_create, const BLE_DEVICE_CONFIG*, config); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , void, BLEIO_gatt_destroy, BLEIO_GATT_HANDLE, bleio_gatt_handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , int, BLEIO_gatt_connect, BLEIO_GATT_HANDLE, bleio_gatt_handle, ON_BLEIO_GATT_CONNECT_COMPLETE, on_bleio_gatt_connect_complete, void*, callback_context); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , void, BLEIO_gatt_disconnect, BLEIO_GATT_HANDLE, bleio_gatt_handle, ON_BLEIO_GATT_DISCONNECT_COMPLETE, on_bleio_gatt_disconnect_complete, void*, callback_context); + +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEMocks, , BLEIO_SEQ_HANDLE, BLEIO_Seq_Create, BLEIO_GATT_HANDLE, bleio_gatt_handle, VECTOR_HANDLE, instructions, ON_BLEIO_SEQ_READ_COMPLETE, on_read_complete, ON_BLEIO_SEQ_WRITE_COMPLETE, on_write_complete); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEMocks, , void, BLEIO_Seq_Destroy, BLEIO_SEQ_HANDLE, bleio_seq_handle, ON_BLEIO_SEQ_DESTROY_COMPLETE, on_destroy_complete, void*, context); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEMocks, , BLEIO_SEQ_RESULT, BLEIO_Seq_Run, BLEIO_SEQ_HANDLE, bleio_seq_handle); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEMocks, , BLEIO_SEQ_RESULT, BLEIO_Seq_AddInstruction, BLEIO_SEQ_HANDLE, bleio_seq_handle, BLEIO_SEQ_INSTRUCTION*, instruction); + +BEGIN_TEST_SUITE(ble_unittests) +TEST_SUITE_INITIALIZE(TestClassInitialize) +{ + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); +} + +TEST_SUITE_CLEANUP(TestClassCleanup) +{ + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); +} + +TEST_FUNCTION_INITIALIZE(TestMethodInitialize) +{ + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } +} + +TEST_FUNCTION_CLEANUP(TestMethodCleanup) +{ + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } +} + +END_TEST_SUITE(ble_unittests) \ No newline at end of file diff --git a/modules/ble/tests/ble_unittests/main.c b/modules/ble/tests/ble_unittests/main.c new file mode 100644 index 00000000..00e9c769 --- /dev/null +++ b/modules/ble/tests/ble_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(ble_unittests, failedTestCount); + return failedTestCount; +} diff --git a/modules/ble/tests/bleio_seq_unittests/CMakeLists.txt b/modules/ble/tests/bleio_seq_unittests/CMakeLists.txt new file mode 100644 index 00000000..0ae5b073 --- /dev/null +++ b/modules/ble/tests/bleio_seq_unittests/CMakeLists.txt @@ -0,0 +1,72 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for bleio_seq_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName bleio_seq_unittests) + +if(LINUX) + set(${theseTestsName}_cpp_files + ${theseTestsName}_linux.cpp + ) + + # Include GIO headers/libs + include_directories(${GWGIOUNIX_INCLUDE_DIRS}) + set(LIBS ${GWGIOUNIX_LIBRARIES}) + + # Blue-z dbus generated sources + set(bluez_headers + ../../deps/linux/dbus-bluez/inc/bluez_characteristic.h + ../../deps/linux/dbus-bluez/inc/bluez_device.h + ) + include_directories(../../deps/linux/dbus-bluez/inc) + + # BLE GATT I/O sources + set(bleio_seq_test_sources + ../../src/bleio_seq_linux.c + ../../src/bleio_seq_linux_schedule_periodic.c + ../../src/bleio_seq_linux_schedule_read.c + ../../src/bleio_seq_linux_schedule_write.c + ) + set(bleio_seq_test_headers + ${bluez_headers} + ../../inc/gio_async_seq.h + ../../inc/bleio_seq.h + ../../inc/bleio_seq_linux_common.h + ) +elseif(WIN32) + set(${theseTestsName}_cpp_files + ${theseTestsName}_windows.cpp + ) + + set(bleio_seq_test_sources + ../../src/bleio_seq_windows.c + ) +endif() + +set(bleio_seq_test_headers + ${bleio_seq_test_headers} + ../../inc/ble_gatt_io.h +) + +include_directories( + ../../inc + ${GW_INC} +) + +set(${theseTestsName}_c_files + ${bleio_seq_test_sources} +) + +set(${theseTestsName}_h_files + ${bleio_seq_test_headers} +) + +if(WIN32) + build_test_artifacts(${theseTestsName} ON) +else() + build_test_artifacts(${theseTestsName} ON ADDITIONAL_LIBS ${LIBS}) +endif() + diff --git a/modules/ble/tests/bleio_seq_unittests/bleio_seq_unittests_linux.cpp b/modules/ble/tests/bleio_seq_unittests/bleio_seq_unittests_linux.cpp new file mode 100644 index 00000000..f914a287 --- /dev/null +++ b/modules/ble/tests/bleio_seq_unittests/bleio_seq_unittests_linux.cpp @@ -0,0 +1,3293 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +#include "bluez_device.h" +#include "bluez_characteristic.h" +#include "gio_async_seq.h" +#include "ble_gatt_io.h" +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/strings.h" +#include "bleio_seq.h" + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit + +#include "../../src/gio_async_seq.c" +#include "vector.c" +#include "buffer.c" +#include "strings.c" +}; + +DEFINE_MICROMOCK_ENUM_TO_STRING(BLEIO_SEQ_RESULT, BLEIO_SEQ_RESULT_VALUES); + +struct +{ + BLEIO_GATT_RESULT result; + const unsigned char* buffer; + size_t size; +} BLEIO_gatt_read_char_by_uuid_results; + +struct +{ + BLEIO_GATT_RESULT result; +} BLEIO_gatt_write_char_by_uuid_results; + +gboolean g_expected_timer_return_value = TRUE; +GSourceFunc g_timer_callback = NULL; +gpointer g_timer_data = NULL; + +TYPED_MOCK_CLASS(CBLEIOSeqMocks, CGlobalMock) +{ +public: + + // memory + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_5(, void, on_write_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context, const char*, characteristic_uuid, BLEIO_SEQ_INSTRUCTION_TYPE, type, BLEIO_SEQ_RESULT, result2) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_6(, void, on_read_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context, const char*, characteristic_uuid, BLEIO_SEQ_INSTRUCTION_TYPE, type, BLEIO_SEQ_RESULT, result2, BUFFER_HANDLE, data) + if (data != NULL) + { + BUFFER_delete(data); + } + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, void, on_destroy_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, VECTOR_HANDLE, VECTOR_create, size_t, elementSize) + auto result2 = BASEIMPLEMENTATION::VECTOR_create(elementSize); + MOCK_METHOD_END(VECTOR_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, size_t, VECTOR_size, VECTOR_HANDLE, vector) + size_t result2 = BASEIMPLEMENTATION::VECTOR_size(vector); + MOCK_METHOD_END(size_t, result2) + + MOCK_STATIC_METHOD_3(, int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements) + auto result2 = BASEIMPLEMENTATION::VECTOR_push_back(handle, elements, numElements); + MOCK_METHOD_END(int, result2) + + MOCK_STATIC_METHOD_2(, void*, VECTOR_element, VECTOR_HANDLE, vector, size_t, index) + void* result2 = BASEIMPLEMENTATION::VECTOR_element(vector, index); + MOCK_METHOD_END(void*, result2) + + MOCK_STATIC_METHOD_1(, void, VECTOR_destroy, VECTOR_HANDLE, vector) + BASEIMPLEMENTATION::VECTOR_destroy(vector); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, size_t, STRING_length, STRING_HANDLE, handle) + auto result2 = BASEIMPLEMENTATION::STRING_length(handle); + MOCK_METHOD_END(size_t, result2) + + MOCK_STATIC_METHOD_1(, void, STRING_delete, STRING_HANDLE, handle) + BASEIMPLEMENTATION::STRING_delete(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, const char*, STRING_c_str, STRING_HANDLE, handle) + auto result2 = BASEIMPLEMENTATION::STRING_c_str(handle); + MOCK_METHOD_END(const char*, result2) + + MOCK_STATIC_METHOD_1(, STRING_HANDLE, STRING_construct, const char*, psz) + auto result2 = BASEIMPLEMENTATION::STRING_construct(psz); + MOCK_METHOD_END(STRING_HANDLE, result2) + + MOCK_STATIC_METHOD_2(, BUFFER_HANDLE, BUFFER_create, const unsigned char*, source, size_t, size) + BUFFER_HANDLE result2 = BASEIMPLEMENTATION::BUFFER_create(source, size); + MOCK_METHOD_END(BUFFER_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, BUFFER_delete, BUFFER_HANDLE, handle) + BASEIMPLEMENTATION::BUFFER_delete(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, unsigned char*, BUFFER_u_char, BUFFER_HANDLE, handle) + auto result2 = BASEIMPLEMENTATION::BUFFER_u_char(handle); + MOCK_METHOD_END(unsigned char*, result2) + + MOCK_STATIC_METHOD_1(, size_t, BUFFER_length, BUFFER_HANDLE, handle) + auto result2 = BASEIMPLEMENTATION::BUFFER_length(handle); + MOCK_METHOD_END(size_t, result2) + + MOCK_STATIC_METHOD_4(, int, BLEIO_gatt_read_char_by_uuid, BLEIO_GATT_HANDLE, bleio_gatt_handle, const char*, ble_uuid, ON_BLEIO_GATT_ATTRIB_READ_COMPLETE, on_bleio_gatt_attrib_read_complete, void*, callback_context) + int result2 = 0; + on_bleio_gatt_attrib_read_complete( + bleio_gatt_handle, + callback_context, + BLEIO_gatt_read_char_by_uuid_results.result, + BLEIO_gatt_read_char_by_uuid_results.buffer, + BLEIO_gatt_read_char_by_uuid_results.size + ); + MOCK_METHOD_END(int, result2) + + MOCK_STATIC_METHOD_6(, int, BLEIO_gatt_write_char_by_uuid, BLEIO_GATT_HANDLE, bleio_gatt_handle, const char*, ble_uuid, const unsigned char*, buffer, size_t, size, ON_BLEIO_GATT_ATTRIB_WRITE_COMPLETE, on_bleio_gatt_attrib_write_complete, void*, callback_context) + int result2 = 0; + on_bleio_gatt_attrib_write_complete( + bleio_gatt_handle, + callback_context, + BLEIO_gatt_write_char_by_uuid_results.result + ); + MOCK_METHOD_END(int, result2) + + MOCK_STATIC_METHOD_1(, void, BLEIO_gatt_destroy, BLEIO_GATT_HANDLE, bleio_gatt_handle) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, guint, g_timeout_add, guint, interval, GSourceFunc, function, gpointer, data) + g_timer_callback = function; + g_timer_data = data; + auto timer_continue = function(data); + ASSERT_ARE_EQUAL(int, g_expected_timer_return_value, timer_continue); + guint result2 = 1; + MOCK_METHOD_END(guint, result2) +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , void, gballoc_free, void*, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_5(CBLEIOSeqMocks, , void, on_write_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context, const char*, characteristic_uuid, BLEIO_SEQ_INSTRUCTION_TYPE, type, BLEIO_SEQ_RESULT, result); +DECLARE_GLOBAL_MOCK_METHOD_6(CBLEIOSeqMocks, , void, on_read_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context, const char*, characteristic_uuid, BLEIO_SEQ_INSTRUCTION_TYPE, type, BLEIO_SEQ_RESULT, result, BUFFER_HANDLE, data); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEIOSeqMocks, , void, on_destroy_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , VECTOR_HANDLE, VECTOR_create, size_t, elementSize); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , void, VECTOR_destroy, VECTOR_HANDLE, vector); +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEIOSeqMocks, , void*, VECTOR_element, VECTOR_HANDLE, vector, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , size_t, VECTOR_size, VECTOR_HANDLE, vector); +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEIOSeqMocks, , int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements); + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , STRING_HANDLE, STRING_construct, const char*, psz); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , size_t, STRING_length, STRING_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , void, STRING_delete, STRING_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , const char*, STRING_c_str, STRING_HANDLE, handle); + +DECLARE_GLOBAL_MOCK_METHOD_2(CBLEIOSeqMocks, , BUFFER_HANDLE, BUFFER_create, const unsigned char*, source, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , void, BUFFER_delete, BUFFER_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , unsigned char*, BUFFER_u_char, BUFFER_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , size_t, BUFFER_length, BUFFER_HANDLE, handle); + +DECLARE_GLOBAL_MOCK_METHOD_4(CBLEIOSeqMocks, , int, BLEIO_gatt_read_char_by_uuid, BLEIO_GATT_HANDLE, bleio_gatt_handle, const char*, ble_uuid, ON_BLEIO_GATT_ATTRIB_READ_COMPLETE, on_bleio_gatt_attrib_read_complete, void*, callback_context); +DECLARE_GLOBAL_MOCK_METHOD_6(CBLEIOSeqMocks, , int, BLEIO_gatt_write_char_by_uuid, BLEIO_GATT_HANDLE, bleio_gatt_handle, const char*, ble_uuid, const unsigned char*, buffer, size_t, size, ON_BLEIO_GATT_ATTRIB_WRITE_COMPLETE, on_bleio_gatt_attrib_write_complete, void*, callback_context); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , void, BLEIO_gatt_destroy, BLEIO_GATT_HANDLE, bleio_gatt_handle); + +DECLARE_GLOBAL_MOCK_METHOD_3(CBLEIOSeqMocks, , guint, g_timeout_add, guint, interval, GSourceFunc, function, gpointer, data); + +BEGIN_TEST_SUITE(bleio_seq_unittests) + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + + g_timer_callback = NULL; + g_timer_data = NULL; + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + } + + /*Tests_SRS_BLEIO_SEQ_13_001: [ BLEIO_Seq_Create shall return NULL if bleio_gatt_handle is NULL. ]*/ + TEST_FUNCTION(BLEIO_Seq_Create_returns_NULL_for_NULL_input1) + { + ///arrange + CBLEIOSeqMocks mocks; + + ///act + auto result = BLEIO_Seq_Create(NULL, (VECTOR_HANDLE)0x42, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_002: [ BLEIO_Seq_Create shall return NULL if instructions is NULL. ]*/ + TEST_FUNCTION(BLEIO_Seq_Create_returns_NULL_for_NULL_input2) + { + ///arrange + CBLEIOSeqMocks mocks; + + ///act + auto result = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, NULL, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_003: [ BLEIO_Seq_Create shall return NULL if the vector instructions is empty. ]*/ + TEST_FUNCTION(BLEIO_Seq_Create_returns_NULL_when_instructions_vector_is_empty) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); + + ///act + auto result = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(instructions); + } + + /*Tests_SRS_BLEIO_SEQ_13_025: [ BLEIO_Seq_Create shall return NULL if the characteristic_uuid field for any instruction is NULL or empty. ]*/ + TEST_FUNCTION(BLEIO_Seq_Create_returns_NULL_when_an_instruction_has_NULL_charid) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + READ_ONCE, + NULL, // NULL characteristic ID + NULL, + { 0 } + }; + VECTOR_push_back(instructions, &instr1, 1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Create + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in validate_instructions + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + + ///act + auto result = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(instructions); + } + + /*Tests_SRS_BLEIO_SEQ_13_025: [ BLEIO_Seq_Create shall return NULL if the characteristic_uuid field for any instruction is NULL or empty. ]*/ + TEST_FUNCTION(BLEIO_Seq_Create_returns_NULL_when_an_instruction_has_empty_charid) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct(""), // empty characteristic ID + NULL, + { 0 } + }; + VECTOR_push_back(instructions, &instr1, 1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Create + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in validate_instructions + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, STRING_length(instr1.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(instructions); + STRING_delete(instr1.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_023: [ BLEIO_Seq_Create shall return NULL if a READ_PERIODIC instruction's interval_in_ms field is zero. ]*/ + TEST_FUNCTION(BLEIO_Seq_Create_returns_NULL_when_an_read_periodic_instruction_has_zero_interval) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + NULL, + { 0 } // zero interval + }; + VECTOR_push_back(instructions, &instr1, 1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Create + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in validate_instructions + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, STRING_length(instr1.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(instructions); + STRING_delete(instr1.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_024: [ BLEIO_Seq_Create shall return NULL if a WRITE_AT_INIT or a WRITE_AT_EXIT or a WRITE_ONCE instruction has the value zero for the size field or has a NULL value in the buffer field. ]*/ + TEST_FUNCTION(BLEIO_Seq_Create_returns_NULL_when_a_write_init_instruction_has_NULL_buffer) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = NULL } // NULL buffer + }; + VECTOR_push_back(instructions, &instr1, 1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Create + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in validate_instructions + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, STRING_length(instr1.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(instructions); + STRING_delete(instr1.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_024: [ BLEIO_Seq_Create shall return NULL if a WRITE_AT_INIT or a WRITE_AT_EXIT or a WRITE_ONCE instruction has the value zero for the size field or has a NULL value in the buffer field. ]*/ + TEST_FUNCTION(BLEIO_Seq_Create_returns_NULL_when_a_write_at_exit_instruction_has_NULL_buffer) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_EXIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = NULL } // NULL buffer + }; + VECTOR_push_back(instructions, &instr1, 1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Create + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in validate_instructions + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, STRING_length(instr1.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(instructions); + STRING_delete(instr1.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_024: [ BLEIO_Seq_Create shall return NULL if a WRITE_AT_INIT or a WRITE_AT_EXIT or a WRITE_ONCE instruction has the value zero for the size field or has a NULL value in the buffer field. ]*/ + TEST_FUNCTION(BLEIO_Seq_Create_returns_NULL_when_a_write_once_instruction_has_NULL_buffer) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = NULL } // NULL buffer + }; + VECTOR_push_back(instructions, &instr1, 1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Create + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in validate_instructions + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, STRING_length(instr1.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(instructions); + STRING_delete(instr1.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_004: [ BLEIO_Seq_Create shall return NULL if any of the underlying platform calls fail. ]*/ + TEST_FUNCTION(BLEIO_Seq_Create_returns_NULL_when_malloc_fails) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_EXIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instr1, 1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Create + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in validate_instructions + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, STRING_length(instr1.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + VECTOR_destroy(instructions); + BUFFER_delete(instr1.data.buffer); + STRING_delete(instr1.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_005: [ BLEIO_Seq_Create shall return a non-NULL handle on successful execution. ]*/ + TEST_FUNCTION(BLEIO_Seq_Create_succeeds) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instr1, 1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Create + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in validate_instructions + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, STRING_length(instr1.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + BLEIO_Seq_Destroy(result, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_010: [BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if bleio_seq_handle is NULL.]*/ + TEST_FUNCTION(BLEIO_Seq_Run_returns_error_for_NULL_input) + { + ///arrange + CBLEIOSeqMocks mocks; + + ///act + auto result = BLEIO_Seq_Run(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_013: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if BLEIO_Seq_Run was previously called on this handle. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_returns_error_when_state_is_not_idle) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + (void)BLEIO_Seq_Run(handle); + mocks.ResetAllCalls(); + + ///act + auto result = BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_014: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_returns_error_when_malloc_fails_when_scheduling_a_read_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + NULL, + { 0 } + }; + VECTOR_push_back(instructions, &instr1, 1); + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_014: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_returns_error_when_BLEIO_gatt_read_char_by_uuid_fails_when_scheduling_a_read_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + NULL, + { 0 } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .SetFailReturn((int)1); + + ///act + auto result = BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_018: [ When a READ_ONCE or a READ_PERIODIC instruction completes execution this API shall invoke the on_read_complete callback passing in the data that was read along with the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_calls_on_read_complete_with_error_when_BLEIO_gatt_read_char_by_uuid_fails_when_scheduling_a_read_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + NULL, + { 0 } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_read_char_by_uuid to call callback with error + BLEIO_gatt_read_char_by_uuid_results.result = BLEIO_GATT_ERROR; + BLEIO_gatt_read_char_by_uuid_results.buffer = NULL; + BLEIO_gatt_read_char_by_uuid_results.size = 0; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, on_read_complete(handle, NULL, fake_char_id, READ_ONCE, BLEIO_SEQ_ERROR, NULL)); + + ///act + (void)BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_018: [ When a READ_ONCE or a READ_PERIODIC instruction completes execution this API shall invoke the on_read_complete callback passing in the data that was read along with the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_015: [ BLEIO_Seq_Run shall schedule execution of all READ_ONCE instructions. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_calls_on_read_complete_with_success_when_BLEIO_gatt_read_char_by_uuid_succeeds) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + NULL, + { 0 } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_read_char_by_uuid to call callback with error + BLEIO_gatt_read_char_by_uuid_results.result = BLEIO_GATT_OK; + BLEIO_gatt_read_char_by_uuid_results.buffer = (unsigned char*)"data"; + BLEIO_gatt_read_char_by_uuid_results.size = 4; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, on_read_complete(handle, NULL, fake_char_id, READ_ONCE, BLEIO_SEQ_OK, IGNORED_PTR_ARG)) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, 4)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + (void)BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_018: [ When a READ_ONCE or a READ_PERIODIC instruction completes execution this API shall invoke the on_read_complete callback passing in the data that was read along with the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_passes_context_for_read) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + (void*)0x42, + { 0 } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_read_char_by_uuid to call callback + BLEIO_gatt_read_char_by_uuid_results.result = BLEIO_GATT_OK; + BLEIO_gatt_read_char_by_uuid_results.buffer = (unsigned char*)"data"; + BLEIO_gatt_read_char_by_uuid_results.size = 4; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, on_read_complete(handle, (void*)0x42, fake_char_id, READ_ONCE, BLEIO_SEQ_OK, IGNORED_PTR_ARG)) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, 4)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + (void)BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_018: [ When a READ_ONCE or a READ_PERIODIC instruction completes execution this API shall invoke the on_read_complete callback passing in the data that was read along with the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_returns_error_when_malloc_fails_when_scheduling_a_read_periodic_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + NULL, + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_017: [ BLEIO_Seq_Run shall create timers at the specified intervals for scheduling execution of all READ_PERIODIC instructions. ]*/ + TEST_FUNCTION(timer_function_returns_G_SOURCE_CONTINUE_when_running_a_read_periodic_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + NULL, + { 500 } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); // in schedule_periodic + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); // in schedule_read + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // in on_read_complete in bleio_seq_linux_schedule_read.c + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); // in schedule_read + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, on_read_complete(handle, NULL, fake_char_id, READ_PERIODIC, BLEIO_SEQ_OK, IGNORED_PTR_ARG)) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, g_timeout_add(500, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, 4)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + // cause BLEIO_gatt_read_char_by_uuid to succeed + BLEIO_gatt_read_char_by_uuid_results.result = BLEIO_GATT_OK; + BLEIO_gatt_read_char_by_uuid_results.buffer = (const unsigned char*)"data"; + BLEIO_gatt_read_char_by_uuid_results.size = 4; + + // the assertion is in the g_timeout_add mock implementation + g_expected_timer_return_value = G_SOURCE_CONTINUE; + + ///act + auto result = BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_OK, result); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + + // call the timer callback once more to cancel the timer + g_expected_timer_return_value = G_SOURCE_REMOVE; + g_timer_callback(g_timer_data); + } + + /*Tests_SRS_BLEIO_SEQ_13_009: [ If there are active instructions of type READ_PERIODIC in progress then the timers associated with those instructions shall be cancelled. ]*/ + TEST_FUNCTION(timer_function_returns_G_SOURCE_REMOVE_after_shutdown_when_running_a_read_periodic_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + NULL, + { 500 } + }; + VECTOR_push_back(instructions, &instr1, 1); + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + + // cause BLEIO_gatt_read_char_by_uuid to succeed + BLEIO_gatt_read_char_by_uuid_results.result = BLEIO_GATT_OK; + BLEIO_gatt_read_char_by_uuid_results.buffer = (const unsigned char*)"data"; + BLEIO_gatt_read_char_by_uuid_results.size = 4; + + g_expected_timer_return_value = G_SOURCE_CONTINUE; + (void)BLEIO_Seq_Run(handle); + BLEIO_Seq_Destroy(handle, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // in on_timer + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy((BLEIO_GATT_HANDLE)0x42)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instr1.characteristic_uuid)); + + ///act + g_expected_timer_return_value = G_SOURCE_REMOVE; + g_timer_callback(g_timer_data); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_016: [ BLEIO_Seq_Run shall schedule execution of all WRITE_AT_INIT instructions. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_014: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_returns_error_when_malloc_fails_when_scheduling_a_write_init_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instr1, 1); + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_033: [ BLEIO_Seq_Run shall schedule execution of all WRITE_ONCE instructions. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_014: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_returns_error_when_malloc_fails_when_scheduling_a_write_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instr1, 1); + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_016: [ BLEIO_Seq_Run shall schedule execution of all WRITE_AT_INIT instructions. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_014: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_returns_error_when_BLEIO_gatt_write_char_by_uuid_fails_when_scheduling_a_write_init_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6) + .SetFailReturn((int)1); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + + ///act + auto result = BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_033: [ BLEIO_Seq_Run shall schedule execution of all WRITE_ONCE instructions. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_014: [ BLEIO_Seq_Run shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_returns_error_when_BLEIO_gatt_write_char_by_uuid_fails_when_scheduling_a_write_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6) + .SetFailReturn((int)1); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + + ///act + auto result = BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_016: [ BLEIO_Seq_Run shall schedule execution of all WRITE_AT_INIT instructions. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_026: [ When the WRITE_AT_INIT instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_020: [ When the WRITE_AT_INIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_calls_on_write_complete_with_error_when_BLEIO_gatt_write_char_by_uuid_fails_when_scheduling_a_write_init_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_ERROR; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, NULL, fake_char_id, WRITE_AT_INIT, BLEIO_SEQ_ERROR)); + + ///act + (void)BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_033: [ BLEIO_Seq_Run shall schedule execution of all WRITE_ONCE instructions. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_035: [ When the WRITE_ONCE instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_034: [ When the WRITE_ONCE instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_calls_on_write_complete_with_error_when_BLEIO_gatt_write_char_by_uuid_fails_when_scheduling_a_write_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_ERROR; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, NULL, fake_char_id, WRITE_ONCE, BLEIO_SEQ_ERROR)); + + ///act + (void)BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_016: [ BLEIO_Seq_Run shall schedule execution of all WRITE_AT_INIT instructions. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_026: [ When the WRITE_AT_INIT instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_020: [ When the WRITE_AT_INIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_calls_on_write_complete_with_success_when_BLEIO_gatt_write_char_by_uuid_succeeds_for_write_at_init) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, NULL, fake_char_id, WRITE_AT_INIT, BLEIO_SEQ_OK)); + + ///act + (void)BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_033: [ BLEIO_Seq_Run shall schedule execution of all WRITE_ONCE instructions. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_035: [ When the WRITE_ONCE instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_034: [ When the WRITE_ONCE instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_calls_on_write_complete_with_success_when_BLEIO_gatt_write_char_by_uuid_succeeds_for_write_once) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, NULL, fake_char_id, WRITE_ONCE, BLEIO_SEQ_OK)); + + ///act + (void)BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_016: [ BLEIO_Seq_Run shall schedule execution of all WRITE_AT_INIT instructions. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_026: [ When the WRITE_AT_INIT instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_020: [ When the WRITE_AT_INIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_passes_context_for_write_at_init) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + (void*)0x42, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, (void*)0x42, fake_char_id, WRITE_AT_INIT, BLEIO_SEQ_OK)); + + ///act + (void)BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_033: [ BLEIO_Seq_Run shall schedule execution of all WRITE_ONCE instructions. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_035: [ When the WRITE_ONCE instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_034: [ When the WRITE_ONCE instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_passes_context_for_write_once) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + (void*)0x42, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Run + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, (void*)0x42, fake_char_id, WRITE_ONCE, BLEIO_SEQ_OK)); + + ///act + (void)BLEIO_Seq_Run(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(handle, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_011: [ BLEIO_Seq_Destroy shall schedule the execution of all WRITE_AT_EXIT instructions.*/ + /*Tests_SRS_BLEIO_SEQ_13_006: [ BLEIO_Seq_Destroy shall free all resources associated with the handle once all the pending I/O operations are complete. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_027: [ When the WRITE_AT_EXIT instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + TEST_FUNCTION(BLEIO_Seq_Destroy_schedules_write_at_exit_and_frees_resources) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_EXIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(instructions)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy((BLEIO_GATT_HANDLE)0x42)); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, NULL, fake_char_id, WRITE_AT_EXIT, BLEIO_SEQ_OK)); + + ///act + BLEIO_Seq_Destroy(handle, NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_011: [ BLEIO_Seq_Destroy shall schedule the execution of all WRITE_AT_EXIT instructions.*/ + /*Tests_SRS_BLEIO_SEQ_13_006: [ BLEIO_Seq_Destroy shall free all resources associated with the handle once all the pending I/O operations are complete. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_027: [ When the WRITE_AT_EXIT instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_031: [ If on_destroy_complete is not NULL then BLEIO_Seq_Destroy shall invoke on_destroy_complete once all WRITE_AT_EXIT instructions have been executed. ]*/ + TEST_FUNCTION(BLEIO_Seq_Destroy_calls_on_destroy_complete) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_EXIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(instructions)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy((BLEIO_GATT_HANDLE)0x42)); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, NULL, fake_char_id, WRITE_AT_EXIT, BLEIO_SEQ_OK)); + STRICT_EXPECTED_CALL(mocks, on_destroy_complete(handle, NULL)); + + ///act + BLEIO_Seq_Destroy(handle, on_destroy_complete, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_011: [ BLEIO_Seq_Destroy shall schedule the execution of all WRITE_AT_EXIT instructions.*/ + /*Tests_SRS_BLEIO_SEQ_13_006: [ BLEIO_Seq_Destroy shall free all resources associated with the handle once all the pending I/O operations are complete. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_027: [ When the WRITE_AT_EXIT instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_031: [ If on_destroy_complete is not NULL then BLEIO_Seq_Destroy shall invoke on_destroy_complete once all WRITE_AT_EXIT instructions have been executed. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_032: [ If on_destroy_complete is not NULL then BLEIO_Seq_Destroy shall pass context as-is to on_destroy_complete. ]*/ + TEST_FUNCTION(BLEIO_Seq_Destroy_calls_on_destroy_complete_and_passes_context) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_EXIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(instructions)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy((BLEIO_GATT_HANDLE)0x42)); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, NULL, fake_char_id, WRITE_AT_EXIT, BLEIO_SEQ_OK)); + STRICT_EXPECTED_CALL(mocks, on_destroy_complete(handle, (void*)0x42)); + + ///act + BLEIO_Seq_Destroy(handle, on_destroy_complete, (void*)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_011: [ BLEIO_Seq_Destroy shall schedule the execution of all WRITE_AT_EXIT instructions.*/ + /*Tests_SRS_BLEIO_SEQ_13_021: [ When the WRITE_AT_EXIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_027: [ When the WRITE_AT_EXIT instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + TEST_FUNCTION(BLEIO_Seq_Destroy_passes_context_for_write_at_exit) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instr1 = + { + WRITE_AT_EXIT, + STRING_construct("fake_char_id"), + (void*)0x42, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + const char* fake_char_id = STRING_c_str(instr1.characteristic_uuid); + VECTOR_push_back(instructions, &instr1, 1); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + auto handle = BLEIO_Seq_Create( + (BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(instructions)); // in BLEIO_Seq_Destroy + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instr1.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy((BLEIO_GATT_HANDLE)0x42)); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instr1.data.buffer)); + STRICT_EXPECTED_CALL(mocks, on_write_complete(handle, (void*)0x42, fake_char_id, WRITE_AT_EXIT, BLEIO_SEQ_OK)); + + ///act + BLEIO_Seq_Destroy(handle, NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_007: [ If bleio_seq_handle is NULL then BLEIO_Seq_Destroy shall do nothing. ]*/ + TEST_FUNCTION(BLEIO_Seq_Destroy_does_nothing_for_NULL_input) + { + ///arrange + CBLEIOSeqMocks mocks; + + ///act + BLEIO_Seq_Destroy(NULL, NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_036: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if bleio_seq_handle is NULL. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_for_NULL_bleio_seq_handle) + { + ///arrange + CBLEIOSeqMocks mocks; + + ///act + auto result = BLEIO_Seq_AddInstruction(NULL, (BLEIO_SEQ_INSTRUCTION*)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_046: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if instruction is NULL. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_for_NULL_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + + ///act + auto result = BLEIO_Seq_AddInstruction((BLEIO_SEQ_HANDLE)0x42, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_049: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if the characteristic_uuid field for the instruction is NULL or empty. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_instruction_has_NULL_charid) + { + ///arrange + CBLEIOSeqMocks mocks; + BLEIO_SEQ_INSTRUCTION instruction = + { + READ_ONCE, + NULL, // NULL characteristic ID + NULL, + { 0 } + }; + + ///act + auto result = BLEIO_Seq_AddInstruction((BLEIO_SEQ_HANDLE)0x42, &instruction); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_049: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if the characteristic_uuid field for the instruction is NULL or empty. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_instruction_has_empty_charid) + { + ///arrange + CBLEIOSeqMocks mocks; + BLEIO_SEQ_INSTRUCTION instruction = + { + READ_ONCE, + STRING_construct(""), // empty characteristic ID + NULL, + { 0 } + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_AddInstruction((BLEIO_SEQ_HANDLE)0x42, &instruction); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + STRING_delete(instruction.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_047: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if a READ_PERIODIC instruction's interval_in_ms field is zero. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_read_periodic_instruction_has_zero_interval) + { + ///arrange + CBLEIOSeqMocks mocks; + BLEIO_SEQ_INSTRUCTION instruction = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + NULL, + { 0 } // zero interval + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_AddInstruction((BLEIO_SEQ_HANDLE)0x42, &instruction); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + STRING_delete(instruction.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_048: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if a WRITE_AT_INIT or a WRITE_AT_EXIT or a WRITE_ONCE instruction has a NULL value in the buffer field. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_a_write_at_init_instruction_has_NULL_buffer) + { + ///arrange + CBLEIOSeqMocks mocks; + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = NULL } // NULL buffer + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_AddInstruction((BLEIO_SEQ_HANDLE)0x42, &instruction); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + STRING_delete(instruction.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_048: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if a WRITE_AT_INIT or a WRITE_AT_EXIT or a WRITE_ONCE instruction has a NULL value in the buffer field. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_a_write_at_exit_instruction_has_NULL_buffer) + { + ///arrange + CBLEIOSeqMocks mocks; + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_AT_EXIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = NULL } // NULL buffer + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_AddInstruction((BLEIO_SEQ_HANDLE)0x42, &instruction); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + STRING_delete(instruction.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_048: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if a WRITE_AT_INIT or a WRITE_AT_EXIT or a WRITE_ONCE instruction has a NULL value in the buffer field. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_a_write_once_instruction_has_NULL_buffer) + { + ///arrange + CBLEIOSeqMocks mocks; + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = NULL } // NULL buffer + }; + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_AddInstruction((BLEIO_SEQ_HANDLE)0x42, &instruction); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + STRING_delete(instruction.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_045: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if BLEIO_Seq_Run was NOT called first. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_seq_is_not_running) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction.characteristic_uuid)); + + ///act + auto result = BLEIO_Seq_AddInstruction(sequence, &instruction); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_037: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_malloc_fails) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + auto result = BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + STRING_delete(instruction2.characteristic_uuid); + BUFFER_delete(instruction2.data.buffer); + } + + /*Tests_SRS_BLEIO_SEQ_13_037: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_malloc_fails_when_scheduling_a_read_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + NULL, + { 0 } + }; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto result = BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + STRING_delete(instruction2.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_037: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_BLEIO_gatt_read_char_by_uuid_fails_when_scheduling_a_read_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + NULL, + { 0 } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .SetFailReturn((int)1); + + ///act + auto result = BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + STRING_delete(instruction2.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_040: [ When a READ_ONCE or a READ_PERIODIC instruction completes execution this API shall invoke the on_read_complete callback passing in the data that was read along with the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_calls_on_read_complete_with_error_when_BLEIO_gatt_read_char_by_uuid_fails_when_scheduling_a_read_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + // cause BLEIO_gatt_read_char_by_uuid to call callback with error + BLEIO_gatt_read_char_by_uuid_results.result = BLEIO_GATT_ERROR; + BLEIO_gatt_read_char_by_uuid_results.buffer = NULL; + BLEIO_gatt_read_char_by_uuid_results.size = 0; + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + NULL, + { 0 } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, on_read_complete(sequence, NULL, fake_char_id, READ_ONCE, BLEIO_SEQ_ERROR, NULL)); + + ///act + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_040: [ When a READ_ONCE or a READ_PERIODIC instruction completes execution this API shall invoke the on_read_complete callback passing in the data that was read along with the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_calls_on_read_complete_with_success_when_BLEIO_gatt_read_char_by_uuid_succeeds) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + // cause BLEIO_gatt_read_char_by_uuid to call callback with error + BLEIO_gatt_read_char_by_uuid_results.result = BLEIO_GATT_OK; + BLEIO_gatt_read_char_by_uuid_results.buffer = (unsigned char*)"data"; + BLEIO_gatt_read_char_by_uuid_results.size = 4; + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + NULL, + { 0 } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, 4)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, on_read_complete(sequence, NULL, fake_char_id, READ_ONCE, BLEIO_SEQ_OK, IGNORED_PTR_ARG)) + .IgnoreArgument(6); + + ///act + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_040: [ When a READ_ONCE or a READ_PERIODIC instruction completes execution this API shall invoke the on_read_complete callback passing in the data that was read along with the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_passes_context_for_read) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + // cause BLEIO_gatt_read_char_by_uuid to call callback with error + BLEIO_gatt_read_char_by_uuid_results.result = BLEIO_GATT_OK; + BLEIO_gatt_read_char_by_uuid_results.buffer = (unsigned char*)"data"; + BLEIO_gatt_read_char_by_uuid_results.size = 4; + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + READ_ONCE, + STRING_construct("fake_char_id"), + (void*)0x42, + { 0 } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, 4)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, on_read_complete(sequence, (void*)0x42, fake_char_id, READ_ONCE, BLEIO_SEQ_OK, IGNORED_PTR_ARG)) + .IgnoreArgument(6); + + ///act + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_037: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_malloc_fails_when_scheduling_a_read_periodic_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + NULL, + { 500 } + }; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto result = BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + STRING_delete(instruction2.characteristic_uuid); + } + + /*Tests_SRS_BLEIO_SEQ_13_039: [ BLEIO_Seq_AddInstruction shall create a timer at the specified interval if the instruction is a READ_PERIODIC instruction. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_timer_function_returns_G_SOURCE_CONTINUE_when_running_a_read_periodic_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + NULL, + { 500 } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_read_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4); // in schedule_read + + STRICT_EXPECTED_CALL(mocks, BUFFER_create(IGNORED_PTR_ARG, 4)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, g_timeout_add(500, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + + STRICT_EXPECTED_CALL(mocks, on_read_complete(sequence, NULL, fake_char_id, READ_PERIODIC, BLEIO_SEQ_OK, IGNORED_PTR_ARG)) + .IgnoreArgument(6); + + // cause BLEIO_gatt_read_char_by_uuid to succeed + BLEIO_gatt_read_char_by_uuid_results.result = BLEIO_GATT_OK; + BLEIO_gatt_read_char_by_uuid_results.buffer = (const unsigned char*)"data"; + BLEIO_gatt_read_char_by_uuid_results.size = 4; + + // the assertion is in the g_timeout_add mock implementation + g_expected_timer_return_value = G_SOURCE_CONTINUE; + + ///act + auto result = BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_OK, result); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + + // call the timer callback once more to cancel the timer + g_expected_timer_return_value = G_SOURCE_REMOVE; + g_timer_callback(g_timer_data); + } + + /*Tests_SRS_BLEIO_SEQ_13_009: [ If there are active instructions of type READ_PERIODIC in progress then the timers associated with those instructions shall be cancelled. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_timer_function_returns_G_SOURCE_REMOVE_after_shutdown_when_running_a_read_periodic_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + READ_PERIODIC, + STRING_construct("fake_char_id"), + NULL, + { 500 } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + // cause BLEIO_gatt_read_char_by_uuid to succeed + BLEIO_gatt_read_char_by_uuid_results.result = BLEIO_GATT_OK; + BLEIO_gatt_read_char_by_uuid_results.buffer = (const unsigned char*)"data"; + BLEIO_gatt_read_char_by_uuid_results.size = 4; + + // the assertion is in the g_timeout_add mock implementation + g_expected_timer_return_value = G_SOURCE_CONTINUE; + + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + BLEIO_Seq_Destroy(sequence, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // in on_timer + + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); // in dec_ref_handle + STRICT_EXPECTED_CALL(mocks, VECTOR_size(instructions)); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(instructions, 0)); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_destroy((BLEIO_GATT_HANDLE)0x42)); + + STRICT_EXPECTED_CALL(mocks, STRING_delete(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instruction.characteristic_uuid)); + + ///act + g_expected_timer_return_value = G_SOURCE_REMOVE; + g_timer_callback(g_timer_data); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_037: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_malloc_fails_when_scheduling_a_write_init_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto result = BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + STRING_delete(instruction2.characteristic_uuid); + BUFFER_delete(instruction2.data.buffer); + } + + /*Tests_SRS_BLEIO_SEQ_13_037: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_malloc_fails_when_scheduling_a_write_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto result = BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + STRING_delete(instruction2.characteristic_uuid); + BUFFER_delete(instruction2.data.buffer); + } + + /*Tests_SRS_BLEIO_SEQ_13_037: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_BLEIO_gatt_write_char_by_uuid_fails_when_scheduling_a_write_init_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6) + .SetFailReturn((int)1); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instruction2.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instruction2.data.buffer)); + + ///act + auto result = BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + STRING_delete(instruction2.characteristic_uuid); + BUFFER_delete(instruction2.data.buffer); + } + + /*Tests_SRS_BLEIO_SEQ_13_037: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_BLEIO_gatt_write_char_by_uuid_fails_when_scheduling_a_write_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6) + .SetFailReturn((int)1); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instruction2.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instruction2.data.buffer)); + + ///act + auto result = BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + STRING_delete(instruction2.characteristic_uuid); + BUFFER_delete(instruction2.data.buffer); + } + + /*Tests_SRS_BLEIO_SEQ_13_042: [ When a WRITE_ONCE or a WRITE_AT_INIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_041: [ When a WRITE_AT_INIT or a WRITE_ONCE instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_calls_on_write_complete_with_error_when_BLEIO_gatt_write_char_by_uuid_fails_when_scheduling_a_write_init_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with error + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_ERROR; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instruction2.data.buffer)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instruction2.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instruction2.data.buffer)); + + STRICT_EXPECTED_CALL(mocks, on_write_complete(sequence, NULL, fake_char_id, WRITE_AT_INIT, BLEIO_SEQ_ERROR)); + + ///act + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_042: [ When a WRITE_ONCE or a WRITE_AT_INIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_041: [ When a WRITE_AT_INIT or a WRITE_ONCE instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_calls_on_write_complete_with_error_when_BLEIO_gatt_write_char_by_uuid_fails_when_scheduling_a_write_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with error + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_ERROR; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instruction2.data.buffer)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instruction2.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instruction2.data.buffer)); + + STRICT_EXPECTED_CALL(mocks, on_write_complete(sequence, NULL, fake_char_id, WRITE_ONCE, BLEIO_SEQ_ERROR)); + + ///act + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_042: [ When a WRITE_ONCE or a WRITE_AT_INIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_041: [ When a WRITE_AT_INIT or a WRITE_ONCE instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_calls_on_write_complete_with_sucess_when_BLEIO_gatt_write_char_by_uuid_succeeds_when_scheduling_a_write_init_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instruction2.data.buffer)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instruction2.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instruction2.data.buffer)); + + STRICT_EXPECTED_CALL(mocks, on_write_complete(sequence, NULL, fake_char_id, WRITE_AT_INIT, BLEIO_SEQ_OK)); + + ///act + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_042: [ When a WRITE_ONCE or a WRITE_AT_INIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_041: [ When a WRITE_AT_INIT or a WRITE_ONCE instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_calls_on_write_complete_with_sucess_when_BLEIO_gatt_write_char_by_uuid_succeeds_when_scheduling_a_write_once_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(instruction2.data.buffer)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(instruction2.data.buffer)); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(instruction2.data.buffer)); + + STRICT_EXPECTED_CALL(mocks, on_write_complete(sequence, NULL, fake_char_id, WRITE_ONCE, BLEIO_SEQ_OK)); + + ///act + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_042: [ When a WRITE_ONCE or a WRITE_AT_INIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_passes_context_for_write_at_init) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_AT_INIT, + STRING_construct("fake_char_id"), + (void*)0x42, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, on_write_complete(sequence, (void*)0x42, fake_char_id, WRITE_AT_INIT, BLEIO_SEQ_OK)); + + ///act + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_042: [ When a WRITE_ONCE or a WRITE_AT_INIT instruction completes execution this API shall invoke the on_write_complete callback passing in the status of the operation and the callback context that was passed in via the BLEIO_SEQ_INSTRUCTION structure. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_041: [ When a WRITE_AT_INIT or a WRITE_ONCE instruction completes execution this API shall free the buffer that was passed in via the instruction. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_passes_context_for_write_once) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + // cause BLEIO_gatt_write_char_by_uuid to call callback with ok + BLEIO_gatt_write_char_by_uuid_results.result = BLEIO_GATT_OK; + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + (void*)0x42, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_delete(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BUFFER_u_char(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_length(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, BUFFER_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, BLEIO_gatt_write_char_by_uuid((BLEIO_GATT_HANDLE)0x42, fake_char_id, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(3) + .IgnoreArgument(4) + .IgnoreArgument(5) + .IgnoreArgument(6); + STRICT_EXPECTED_CALL(mocks, on_write_complete(sequence, (void*)0x42, fake_char_id, WRITE_ONCE, BLEIO_SEQ_OK)); + + ///act + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_038: [ BLEIO_Seq_AddInstruction shall schedule execution of the instruction. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_schedules_write_at_exit_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_AT_EXIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + ///act + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + } + + /*Tests_SRS_BLEIO_SEQ_13_038: [ BLEIO_Seq_AddInstruction shall schedule execution of the instruction. ]*/ + /*Tests_SRS_BLEIO_SEQ_13_037: [ BLEIO_Seq_AddInstruction shall return BLEIO_SEQ_ERROR if an underlying platform call fails. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error_when_VECTOR_push_back_fails_for_write_at_exit_instruction) + { + ///arrange + CBLEIOSeqMocks mocks; + VECTOR_HANDLE instructions = VECTOR_create(sizeof(BLEIO_SEQ_INSTRUCTION)); + BLEIO_SEQ_INSTRUCTION instruction = + { + WRITE_ONCE, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + VECTOR_push_back(instructions, &instruction, 1); + auto sequence = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, instructions, on_read_complete, on_write_complete); + (void)BLEIO_Seq_Run(sequence); + + BLEIO_SEQ_INSTRUCTION instruction2 = + { + WRITE_AT_EXIT, + STRING_construct("fake_char_id"), + NULL, + { .buffer = BUFFER_create((const unsigned char*)"data", 4) } + }; + + const char* fake_char_id = STRING_c_str(instruction2.characteristic_uuid); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, STRING_length(instruction2.characteristic_uuid)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(instruction2.characteristic_uuid)); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn((int)1); + + ///act + (void)BLEIO_Seq_AddInstruction(sequence, &instruction2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + BLEIO_Seq_Destroy(sequence, NULL, NULL); + STRING_delete(instruction2.characteristic_uuid); + BUFFER_delete(instruction2.data.buffer); + } + +END_TEST_SUITE(bleio_seq_unittests) diff --git a/modules/ble/tests/bleio_seq_unittests/bleio_seq_unittests_windows.cpp b/modules/ble/tests/bleio_seq_unittests/bleio_seq_unittests_windows.cpp new file mode 100644 index 00000000..cd3af343 --- /dev/null +++ b/modules/ble/tests/bleio_seq_unittests/bleio_seq_unittests_windows.cpp @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +#include "azure_c_shared_utility/lock.h" +#include "bleio_seq.h" + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit +}; + +DEFINE_MICROMOCK_ENUM_TO_STRING(BLEIO_SEQ_RESULT, BLEIO_SEQ_RESULT_VALUES); + +TYPED_MOCK_CLASS(CBLEIOSeqMocks, CGlobalMock) +{ +public: + + // memory + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_5(, void, on_write_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context, const char*, characteristic_uuid, BLEIO_SEQ_INSTRUCTION_TYPE, type, BLEIO_SEQ_RESULT, result2) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_6(, void, on_read_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context, const char*, characteristic_uuid, BLEIO_SEQ_INSTRUCTION_TYPE, type, BLEIO_SEQ_RESULT, result2, BUFFER_HANDLE, data) + MOCK_VOID_METHOD_END() +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CBLEIOSeqMocks, , void, gballoc_free, void*, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_5(CBLEIOSeqMocks, , void, on_write_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context, const char*, characteristic_uuid, BLEIO_SEQ_INSTRUCTION_TYPE, type, BLEIO_SEQ_RESULT, result); +DECLARE_GLOBAL_MOCK_METHOD_6(CBLEIOSeqMocks, , void, on_read_complete, BLEIO_SEQ_HANDLE, bleio_seq_handle, void*, context, const char*, characteristic_uuid, BLEIO_SEQ_INSTRUCTION_TYPE, type, BLEIO_SEQ_RESULT, result, BUFFER_HANDLE, data); + +BEGIN_TEST_SUITE(bleio_seq_unittests) + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + } + + /*Tests_SRS_BLEIO_SEQ_13_028: [On Windows, this function shall return NULL.]*/ + TEST_FUNCTION(BLEIO_Seq_Create_returns_NULL) + { + ///arrange + CBLEIOSeqMocks mocks; + + ///act + auto result = BLEIO_Seq_Create((BLEIO_GATT_HANDLE)0x42, (VECTOR_HANDLE)0x42, on_read_complete, on_write_complete); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_029: [ On Windows, this function shall do nothing. ]*/ + TEST_FUNCTION(BLEIO_Seq_Destroy_does_nothing) + { + ///arrange + CBLEIOSeqMocks mocks; + + ///act + BLEIO_Seq_Destroy((BLEIO_SEQ_HANDLE)0x42, NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_030: [ On Windows this function shall return BLEIO_SEQ_ERROR. ]*/ + TEST_FUNCTION(BLEIO_Seq_Run_returns_error) + { + ///arrange + CBLEIOSeqMocks mocks; + + ///act + auto result = BLEIO_Seq_Run((BLEIO_SEQ_HANDLE)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + } + + /*Tests_SRS_BLEIO_SEQ_13_044: [ On Windows this function shall return BLEIO_SEQ_ERROR. ]*/ + TEST_FUNCTION(BLEIO_Seq_AddInstruction_returns_error) + { + ///arrange + CBLEIOSeqMocks mocks; + + ///act + auto result = BLEIO_Seq_AddInstruction((BLEIO_SEQ_HANDLE)0x42, (BLEIO_SEQ_INSTRUCTION*)0x42); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(BLEIO_SEQ_RESULT, BLEIO_SEQ_ERROR, result); + + ///cleanup + } + +END_TEST_SUITE(bleio_seq_unittests) diff --git a/modules/ble/tests/bleio_seq_unittests/main.c b/modules/ble/tests/bleio_seq_unittests/main.c new file mode 100644 index 00000000..b7e28d6e --- /dev/null +++ b/modules/ble/tests/bleio_seq_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(bleio_seq_unittests, failedTestCount); + return failedTestCount; +} diff --git a/modules/ble/tests/gio_async_seq_unittests/CMakeLists.txt b/modules/ble/tests/gio_async_seq_unittests/CMakeLists.txt new file mode 100644 index 00000000..c43a4cfd --- /dev/null +++ b/modules/ble/tests/gio_async_seq_unittests/CMakeLists.txt @@ -0,0 +1,35 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +# this is CMakeLists for the gio_async_seq_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName gio_async_seq_unittests) + +set(${theseTestsName}_cpp_files + ${theseTestsName}.cpp +) + +# Include GIO headers/libs +include_directories(${GWGIOUNIX_INCLUDE_DIRS}) +set(LIBS ${GWGIOUNIX_LIBRARIES}) + +include_directories( + ../../inc + ${GW_INC} +) + +set(${theseTestsName}_c_files + ../../src/gio_async_seq.c +) + +set(${theseTestsName}_h_files + ../../inc/gio_async_seq.h +) + +if(WIN32) + build_test_artifacts(${theseTestsName} ON) +else() + build_test_artifacts(${theseTestsName} ON ADDITIONAL_LIBS ${LIBS}) +endif() diff --git a/modules/ble/tests/gio_async_seq_unittests/gio_async_seq_unittests.cpp b/modules/ble/tests/gio_async_seq_unittests/gio_async_seq_unittests.cpp new file mode 100644 index 00000000..28750cb8 --- /dev/null +++ b/modules/ble/tests/gio_async_seq_unittests/gio_async_seq_unittests.cpp @@ -0,0 +1,1433 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +#include + +#include "gio_async_seq.h" +#include "azure_c_shared_utility/lock.h" + +DEFINE_MICROMOCK_ENUM_TO_STRING(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_RESULT_VALUES); + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit +}; + +typedef void (*PFN_DESTROY_NOTIFY)(gpointer data); + +// This is a mock implementation of GLIB's pointer array type - +// GPtrArray - using std::vector. The first two members +// below MUST be where they are to match GPtrArray's memory layout. +class CGPtrArray +{ +public: + gpointer* pdata; + guint len; + std::vector _pointers; + PFN_DESTROY_NOTIFY _destroy_notify; + +public: + CGPtrArray() : + _destroy_notify(NULL), + len(0), + pdata(NULL) + {} + + CGPtrArray(PFN_DESTROY_NOTIFY destroy_notify) : + _destroy_notify(destroy_notify), + len(0), + pdata(NULL) + {} + + ~CGPtrArray() + { + // invoke the destroy notify function for each item + if (_destroy_notify != NULL) + { + for (auto ptr : _pointers) + { + _destroy_notify(ptr); + } + } + } + + void add(gpointer data) + { + _pointers.push_back(data); + len = _pointers.size(); + pdata = &(_pointers.front()); + } + + void remove_range(guint index, guint length) + { + auto first = _pointers.begin() + index; + auto last = _pointers.begin() + index + length; + + // invoke the destroy notify function for each item + // being removed from the vector + if (_destroy_notify != NULL) + { + for (auto it = first; it != last; ++it) + { + _destroy_notify(*it); + } + } + + _pointers.erase(first, last); + len = _pointers.size(); + pdata = &(_pointers.front()); + } +}; + +//////////////////////////////////////////////////////////// +// The cached copy of the CGPtrArray object in a given test. +static CGPtrArray* g_cached_ptr_array = NULL; + +static GAsyncReadyCallback g_resolve_callback = NULL; + +TYPED_MOCK_CLASS(CGIOAsyncSeqMocks, CGlobalMock) +{ +public: + + // memory + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, GPtrArray*, g_ptr_array_new_with_free_func, GDestroyNotify, element_free_func) + GPtrArray* result2 = (GPtrArray*)new CGPtrArray(element_free_func); + g_cached_ptr_array = (CGPtrArray*)result2; + MOCK_METHOD_END(GPtrArray*, result2); + + MOCK_STATIC_METHOD_1(, void, g_ptr_array_unref, GPtrArray*, array) + CGPtrArray* parr = (CGPtrArray*)array; + delete parr; + g_cached_ptr_array = NULL; + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, void, g_ptr_array_add, GPtrArray*, array, gpointer, data) + CGPtrArray* parr = (CGPtrArray*)array; + parr->add(data); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, GPtrArray*, g_ptr_array_remove_range, GPtrArray*, array, guint, index, guint, length) + CGPtrArray* parr = (CGPtrArray*)array; + parr->remove_range(index, length); + MOCK_METHOD_END(GPtrArray*, array); + + MOCK_STATIC_METHOD_1(, void, g_clear_error, GError**, err) + if (*err != NULL) + { + gchar* msg = (gchar*)*err; + delete[] msg; + *err = NULL; + } + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, GError*, g_error_new_literal, GQuark, domain, gint, code, const gchar*, message) + gchar* msg = new gchar[strlen(message) + 1]; + strcpy(msg, message); + GError* result2 = (GError*)msg; + MOCK_METHOD_END(GError*, result2); + + MOCK_STATIC_METHOD_4(, void, pass_NULL_user_data_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback) + async_callback(NULL, NULL, NULL); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, void, save_async_callback_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback) + g_resolve_callback = async_callback; + async_callback(NULL, NULL, (gpointer)async_seq_handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, void, simple_forwarder_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback) + ASSERT_IS_NOT_NULL(async_callback); + async_callback(NULL, NULL, async_seq_handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, void, add_callbacks_while_running_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback) + auto result2 = GIO_Async_Seq_Add( + async_seq_handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_ERROR, result2); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, void, call_run_while_running_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback) + auto result2 = GIO_Async_Seq_Run_Async(async_seq_handle); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_ERROR, result2); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, void, callback_context_checker_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback) + ASSERT_ARE_EQUAL(void_ptr, callback_context, (gpointer)0x42); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, void, previous_result_checker_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback) + ASSERT_ARE_EQUAL(void_ptr, previous_result, (gpointer)0x42); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, void, previous_result_is_NULL_checker_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback) + ASSERT_IS_NULL(previous_result); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, void, another_simple_forwarder_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback) + ASSERT_IS_NOT_NULL(async_callback); + async_callback(NULL, NULL, async_seq_handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, void, sequence_complete, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, void, sequence_error, GIO_ASYNCSEQ_HANDLE, async_seq_handle, const GError*, err) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, gpointer, simple_forwarder_finish, GIO_ASYNCSEQ_HANDLE, async_seq_handle, GAsyncResult*, result_param, GError**, error) + *error = NULL; + auto result2 = (gpointer)0x42; + MOCK_METHOD_END(gpointer, result2) + + MOCK_STATIC_METHOD_3(, gpointer, finish_with_error_finish, GIO_ASYNCSEQ_HANDLE, async_seq_handle, GAsyncResult*, result_param, GError**, error) + *error = g_error_new_literal( + 0x01, GIO_ASYNCSEQ_ERROR, "Whoops!" + ); + gpointer result2 = NULL; + MOCK_METHOD_END(gpointer, result2) +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CGIOAsyncSeqMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CGIOAsyncSeqMocks, , void, gballoc_free, void*, ptr); +DECLARE_GLOBAL_MOCK_METHOD_1(CGIOAsyncSeqMocks, , GPtrArray*, g_ptr_array_new_with_free_func, GDestroyNotify, element_free_func); +DECLARE_GLOBAL_MOCK_METHOD_1(CGIOAsyncSeqMocks, , void, g_ptr_array_unref, GPtrArray*, array); +DECLARE_GLOBAL_MOCK_METHOD_2(CGIOAsyncSeqMocks, , void, g_ptr_array_add, GPtrArray*, array, gpointer, data); +DECLARE_GLOBAL_MOCK_METHOD_3(CGIOAsyncSeqMocks, , GPtrArray*, g_ptr_array_remove_range, GPtrArray*, array, guint, index, guint, length); +DECLARE_GLOBAL_MOCK_METHOD_1(CGIOAsyncSeqMocks, , void, g_clear_error, GError**, err); +DECLARE_GLOBAL_MOCK_METHOD_3(CGIOAsyncSeqMocks, , GError*, g_error_new_literal, GQuark, domain, gint, code, const gchar*, message); +DECLARE_GLOBAL_MOCK_METHOD_4(CGIOAsyncSeqMocks, , void, pass_NULL_user_data_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback); +DECLARE_GLOBAL_MOCK_METHOD_4(CGIOAsyncSeqMocks, , void, save_async_callback_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback); +DECLARE_GLOBAL_MOCK_METHOD_4(CGIOAsyncSeqMocks, , void, simple_forwarder_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback); +DECLARE_GLOBAL_MOCK_METHOD_4(CGIOAsyncSeqMocks, , void, add_callbacks_while_running_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback); +DECLARE_GLOBAL_MOCK_METHOD_4(CGIOAsyncSeqMocks, , void, call_run_while_running_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback); +DECLARE_GLOBAL_MOCK_METHOD_4(CGIOAsyncSeqMocks, , void, callback_context_checker_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback); +DECLARE_GLOBAL_MOCK_METHOD_4(CGIOAsyncSeqMocks, , void, previous_result_checker_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback); +DECLARE_GLOBAL_MOCK_METHOD_4(CGIOAsyncSeqMocks, , void, previous_result_is_NULL_checker_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback); +DECLARE_GLOBAL_MOCK_METHOD_4(CGIOAsyncSeqMocks, , void, another_simple_forwarder_start, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result, gpointer, callback_context, GAsyncReadyCallback, async_callback); +DECLARE_GLOBAL_MOCK_METHOD_2(CGIOAsyncSeqMocks, , void, sequence_complete, GIO_ASYNCSEQ_HANDLE, async_seq_handle, gpointer, previous_result); +DECLARE_GLOBAL_MOCK_METHOD_2(CGIOAsyncSeqMocks, , void, sequence_error, GIO_ASYNCSEQ_HANDLE, async_seq_handle, const GError*, error); +DECLARE_GLOBAL_MOCK_METHOD_3(CGIOAsyncSeqMocks, , gpointer, simple_forwarder_finish, GIO_ASYNCSEQ_HANDLE, async_seq_handle, GAsyncResult*, result, GError**, error); +DECLARE_GLOBAL_MOCK_METHOD_3(CGIOAsyncSeqMocks, , gpointer, finish_with_error_finish, GIO_ASYNCSEQ_HANDLE, async_seq_handle, GAsyncResult*, result, GError**, error); + +static GIO_ASYNCSEQ_RESULT call_GIO_Async_Seq_Adv( + GIO_ASYNCSEQ_HANDLE async_seq_handle, + gpointer callback_context, + ... +) +{ + GIO_ASYNCSEQ_RESULT result; + va_list args; + va_start(args, callback_context); + result = GIO_Async_Seq_Addv(async_seq_handle, callback_context, args); + va_end(args); + + return result; +} + +BEGIN_TEST_SUITE(gio_async_seq_unittests) + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + + // resets all the counters to zero + g_cached_ptr_array = NULL; + g_resolve_callback = NULL; + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_002: [GIO_Async_Seq_Create shall return NULL when any of the underlying platform calls fail.]*/ + TEST_FUNCTION(GIO_Async_Seq_Create_fails_when_malloc_fails) + { + ///arrange + CGIOAsyncSeqMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + auto result = GIO_Async_Seq_Create(NULL, NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_002: [GIO_Async_Seq_Create shall return NULL when any of the underlying platform calls fail.]*/ + TEST_FUNCTION(GIO_Async_Seq_Create_fails_when_g_ptr_array_new_with_free_func_fails) + { + ///arrange + CGIOAsyncSeqMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_new_with_free_func(gballoc_free)) + .SetFailReturn((GPtrArray*)NULL); + + ///act + auto result = GIO_Async_Seq_Create(NULL, NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_001: [ GIO_Async_Seq_Create shall return a non-NULL handle on successful execution. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Create_succeeds) + { + ///arrange + CGIOAsyncSeqMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_new_with_free_func(gballoc_free)); + + ///act + auto result = GIO_Async_Seq_Create(NULL, NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + + ///cleanup + GIO_Async_Seq_Destroy(result); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_005: [ GIO_Async_Seq_Create shall save the async_seq_context pointer so that it can be retrieved later by calling GIO_Async_Seq_GetContext. ]*/ + /*Tests_SRS_GIO_ASYNCSEQ_13_007: [ GIO_Async_Seq_GetContext shall return the value of the async_seq_context parameter that was passed in when calling GIO_Async_Seq_Create. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Create_saves_context_pointer) + { + ///arrange + gpointer context = (gpointer)0x42; + CGIOAsyncSeqMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_new_with_free_func(gballoc_free)); + + ///act + auto result = GIO_Async_Seq_Create(context, NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NOT_NULL(result); + gpointer actual_context = GIO_Async_Seq_GetContext(result); + ASSERT_ARE_EQUAL(void_ptr, context, actual_context); + + ///cleanup + GIO_Async_Seq_Destroy(result); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_003: [ GIO_Async_Seq_Destroy shall do nothing if async_seq_handle is NULL. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Destroy_does_nothing_if_handle_is_null) + { + ///arrange + CGIOAsyncSeqMocks mocks; + + ///act + GIO_Async_Seq_Destroy(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_004: [ GIO_Async_Seq_Destroy shall free all resources associated with the handle. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Destroy_frees_things) + { + ///arrange + CGIOAsyncSeqMocks mocks; + + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_ptr_array_unref(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + GIO_Async_Seq_Destroy(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_006: [ GIO_Async_Seq_GetContext shall return NULL if async_seq_handle is NULL. ]*/ + TEST_FUNCTION(GIO_Async_Seq_GetContext_returns_NULL_with_NULL_handle) + { + ///arrange + CGIOAsyncSeqMocks mocks; + + ///act + auto result = GIO_Async_Seq_GetContext(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_IS_NULL(result); + + ///cleanup + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_008: [ GIO_Async_Seq_Add shall return GIO_ASYNCSEQ_ERROR if async_seq_handle is NULL. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Add_returns_error_for_NULL_handle) + { + ///arrange + CGIOAsyncSeqMocks mocks; + + ///act + auto result = GIO_Async_Seq_Add(NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_ERROR, result); + + ///cleanup + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_009: [ GIO_Async_Seq_Add shall return GIO_ASYNCSEQ_ERROR if the async sequence's state is not equal to GIO_ASYNCSEQ_STATE_PENDING. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Add_returns_error_if_state_is_not_pending) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, add_callbacks_while_running_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + GIO_Async_Seq_Add( + handle, NULL, + add_callbacks_while_running_start, simple_forwarder_finish, + NULL + ); + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_010: [ GIO_Async_Seq_Add shall append the new async operations to the end of the existing list of async operations if any. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Add_appends_to_end_of_list) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_011: [ GIO_Async_Seq_Add shall process callbacks from the variable arguments list till a callback whose value is NULL is encountered. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Add_check_NULL_sentinel) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL, + // The following pair SHOULD NOT get added + simple_forwarder_start, simple_forwarder_finish + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + auto actual_size = g_cached_ptr_array->_pointers.size(); + ASSERT_ARE_EQUAL(size_t, 1, actual_size); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_012: [ When a GIO_ASYNCSEQ_CALLBACK is encountered in the varargs, the next argument MUST be non-NULL. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Add_callback_pairs_must_match) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + ///act + auto result = GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, NULL + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_ERROR, result); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_013: [ GIO_Async_Seq_Add shall return GIO_ASYNCSEQ_ERROR when any of the underlying platform calls fail. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Add_fails_when_malloc_fails) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + auto result = GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_ERROR, result); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_014: [ The callback_context pointer value shall be saved so that they can be passed to the callbacks later when they are invoked. ]*/ + /*Tests_SRS_GIO_ASYNCSEQ_13_021: [ GIO_Async_Seq_Run shall pass the callback context that was supplied to GIO_Async_Seq_Add or GIO_Async_Seq_Addv when the first operation was added to the sequence when invoking the 'start' callback. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Add_callback_context_is_passed_along) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, callback_context_checker_start(handle, NULL, (gpointer)0x42, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + (void)GIO_Async_Seq_Add( + handle, (gpointer)0x42, + callback_context_checker_start, simple_forwarder_finish, + NULL + ); + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_015: [ The list of callbacks that had been added to the sequence before calling this API will remain unchanged after the function returns when an error occurs. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Add_is_transactional) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_remove_range(IGNORED_PTR_ARG, 1, 1)) + .IgnoreArgument(1); + // let things go haywire when the second function pair + // is being added at which point the internal list should + // have 2 items - one that was added above and one that + // was added for the first function pair in this call + // which should subsequently get rolled back + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + auto result = GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_ERROR, result); + ASSERT_ARE_EQUAL(size_t, 1, g_cached_ptr_array->_pointers.size()); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_016: [ GIO_Async_Seq_Add shall return GIO_ASYNCSEQ_OK if the API executes successfully. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Add_succeeds) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + auto result = GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_OK, result); + ASSERT_ARE_EQUAL(size_t, 2, g_cached_ptr_array->_pointers.size()); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_017: [ GIO_Async_Seq_Run shall return GIO_ASYNCSEQ_ERROR if async_seq_handle is NULL. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Run_Async_returns_error_with_NULL_handle) + { + ///arrange + CGIOAsyncSeqMocks mocks; + + ///act + auto result = GIO_Async_Seq_Run_Async(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_ERROR, result); + + ///cleanup + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_018: [ GIO_Async_Seq_Run shall return GIO_ASYNCSEQ_ERROR if the sequence's state is already GIO_ASYNCSEQ_STATE_RUNNING. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Run_Async_returns_error_if_state_is_running) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + call_run_while_running_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, call_run_while_running_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_019: [ GIO_Async_Seq_Run shall complete the sequence and invoke the sequence's complete callback if there are no asynchronous operations to process. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Run_Async_calls_sequence_complete_callback) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, sequence_complete); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, sequence_complete(handle, NULL)); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_020: [ GIO_Async_Seq_Run shall invoke the 'start' callback of the first async operation in the sequence. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Run_Async_calls_first_callback) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, simple_forwarder_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_031: [ resolve_callback shall supply NULL as the value of the previous_result parameter of the 'start' callback. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Run_Async_first_callback_prev_result_is_null) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + previous_result_is_NULL_checker_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, previous_result_is_NULL_checker_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_023: [ GIO_Async_Seq_Run shall supply a non-NULL pointer to a function as the value of the async_callback parameter when calling the 'start' callback. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Run_Async_start_callback_gets_non_NULL_async_callback) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, simple_forwarder_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_022: [ GIO_Async_Seq_Run shall return GIO_ASYNCSEQ_OK when there are no errors. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Run_Async_succeeds) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, simple_forwarder_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_start(handle, (gpointer)0x42, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + + ///act + auto result = GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_OK, result); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_024: [ resolve_callback shall do nothing else if user_data is NULL. ]*/ + TEST_FUNCTION(resolve_callback_calls_does_nothing_if_user_data_is_null) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + pass_NULL_user_data_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, pass_NULL_user_data_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_025: [ resolve_callback shall invoke the sequence's error callback and suspend execution of the sequence if the state of the sequence is not equal to GIO_ASYNCSEQ_STATE_RUNNING. ]*/ + TEST_FUNCTION(resolve_callback_calls_sequence_error_if_not_running) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, sequence_error, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + save_async_callback_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, save_async_callback_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, sequence_error(handle, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, g_error_new_literal(IGNORED_NUM_ARG, GIO_ASYNCSEQ_ERROR, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, g_clear_error(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + // call the resolve callback function once more; + // this should cause the sequence error handler + // to get called since the sequence is now complete + g_resolve_callback(NULL, NULL, handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_026: [ resolve_callback shall invoke the 'finish' callback of the async operation that was just concluded. ]*/ + /*Tests_SRS_GIO_ASYNCSEQ_13_033: [ resolve_callback shall supply a non-NULL pointer to a function as the value of the async_callback parameter when calling the 'start' callback. ]*/ + TEST_FUNCTION(resolve_callback_calls_finish_callback) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, simple_forwarder_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_027: [ resolve_callback shall invoke the sequence's error callback and suspend execution of the sequence if the 'finish' callback returns a non-NULL GError pointer. ]*/ + /*Tests_SRS_GIO_ASYNCSEQ_13_028: [ resolve_callback shall free the GError object by calling g_clear_error if the 'finish' callback returns a non-NULL GError pointer. ]*/ + TEST_FUNCTION(resolve_callback_calls_sequence_error_when_callback_fails) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, sequence_error, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, finish_with_error_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, g_error_new_literal(IGNORED_NUM_ARG, GIO_ASYNCSEQ_ERROR, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, g_clear_error(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, finish_with_error_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, sequence_error(handle, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_029: [ resolve_callback shall invoke the 'start' callback of the next async operation in the sequence. ]*/ + TEST_FUNCTION(resolve_callback_calls_second_callback_start) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + another_simple_forwarder_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, simple_forwarder_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, another_simple_forwarder_start(handle, (gpointer)0x42, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_030: [ resolve_callback shall supply the result of calling the 'finish' callback of the current async operation as the value of the previous_result parameter of the 'start' callback of the next async operation. ]*/ + TEST_FUNCTION(resolve_callback_passes_result_of_first_callback_to_second_callback) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + previous_result_checker_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, simple_forwarder_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, previous_result_checker_start(handle, (gpointer)0x42, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_032: [ resolve_callback shall pass the callback context that was supplied to GIO_Async_Seq_Add when the next async operation was added to the sequence when invoking the 'start' callback. ]*/ + TEST_FUNCTION(resolve_callback_context_is_passed_along_for_second_async_operation) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + (void)GIO_Async_Seq_Add( + handle, (gpointer)0x42, + callback_context_checker_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, simple_forwarder_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, callback_context_checker_start(handle, (gpointer)0x42, (gpointer)0x42, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_034: [ resolve_callback shall complete the sequence and invoke the sequence's complete callback if there are no more asynchronous operations to process. ]*/ + TEST_FUNCTION(resolve_complete_calls_sequence_complete_callback) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, sequence_complete); + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, simple_forwarder_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_start(handle, (gpointer)0x42, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, simple_forwarder_finish(handle, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, sequence_complete(handle, (gpointer)0x42)); + + ///act + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_035: [ GIO_Async_Seq_Addv shall return GIO_ASYNCSEQ_ERROR if async_seq_handle is NULL. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Addv_returns_error_for_NULL_handle) + { + ///arrange + CGIOAsyncSeqMocks mocks; + + ///act + auto result = call_GIO_Async_Seq_Adv(NULL, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_ERROR, result); + + ///cleanup + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_036: [ GIO_Async_Seq_Addv shall return GIO_ASYNCSEQ_ERROR if the async sequence's state is not equal to GIO_ASYNCSEQ_STATE_PENDING. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Addv_returns_error_if_state_is_not_pending) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, add_callbacks_while_running_start(handle, NULL, NULL, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + call_GIO_Async_Seq_Adv( + handle, NULL, + add_callbacks_while_running_start, simple_forwarder_finish, + NULL + ); + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_037: [ GIO_Async_Seq_Addv shall append the new async operations to the end of the existing list of async operations if any. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Addv_appends_to_end_of_list) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + (void)call_GIO_Async_Seq_Adv( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_038: [ GIO_Async_Seq_Addv shall add callbacks from the variable arguments list till a callback whose value is NULL is encountered. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Addv_check_NULL_sentinel) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + (void)call_GIO_Async_Seq_Adv( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL, + // The following pair SHOULD NOT get added + simple_forwarder_start, simple_forwarder_finish + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + auto actual_size = g_cached_ptr_array->_pointers.size(); + ASSERT_ARE_EQUAL(size_t, 1, actual_size); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_039: [ When a GIO_ASYNCSEQ_CALLBACK is encountered in the varargs, the next argument MUST be non-NULL. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Addv_callback_pairs_must_match) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + ///act + auto result = call_GIO_Async_Seq_Adv( + handle, NULL, + simple_forwarder_start, NULL + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_ERROR, result); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_040: [ GIO_Async_Seq_Addv shall return GIO_ASYNCSEQ_ERROR when any of the underlying platform calls fail. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Addv_fails_when_malloc_fails) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + auto result = call_GIO_Async_Seq_Adv( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_ERROR, result); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_041: [ The callback_context pointer value shall be saved so that they can be passed to the callbacks later when they are invoked. ]*/ + /*Tests_SRS_GIO_ASYNCSEQ_13_021: [ GIO_Async_Seq_Run shall pass the callback context that was supplied to GIO_Async_Seq_Add or GIO_Async_Seq_Addv when the first operation was added to the sequence when invoking the 'start' callback. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Addv_callback_context_is_passed_along) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, callback_context_checker_start(handle, NULL, (gpointer)0x42, IGNORED_PTR_ARG)) + .IgnoreArgument(4); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + (void)call_GIO_Async_Seq_Adv( + handle, (gpointer)0x42, + callback_context_checker_start, simple_forwarder_finish, + NULL + ); + (void)GIO_Async_Seq_Run_Async(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_042: [ The list of callbacks that had been added to the sequence before calling this API will remain unchanged after the function returns when an error occurs. ]*/ + TEST_FUNCTION(GIO_Async_Seq_Addv_is_transactional) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + (void)GIO_Async_Seq_Add( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + mocks.ResetAllCalls(); + + // let things go haywire when the second function pair + // is being added at which point the internal list should + // have 2 items - one that was added above and one that + // was added for the first function pair in this call + // which should subsequently get rolled back + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_remove_range(IGNORED_PTR_ARG, 1, 1)) + .IgnoreArgument(1); + + ///act + auto result = call_GIO_Async_Seq_Adv( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_ERROR, result); + ASSERT_ARE_EQUAL(size_t, 1, g_cached_ptr_array->_pointers.size()); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + + /*Tests_SRS_GIO_ASYNCSEQ_13_043: [GIO_Async_Seq_Addv shall return GIO_ASYNCSEQ_OK if the API executes successfully.]*/ + TEST_FUNCTION(GIO_Async_Seq_Addv_succeeds) + { + ///arrange + CGIOAsyncSeqMocks mocks; + auto handle = GIO_Async_Seq_Create(NULL, NULL, NULL); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, g_ptr_array_add(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + ///act + auto result = call_GIO_Async_Seq_Adv( + handle, NULL, + simple_forwarder_start, simple_forwarder_finish, + simple_forwarder_start, simple_forwarder_finish, + NULL + ); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(GIO_ASYNCSEQ_RESULT, GIO_ASYNCSEQ_OK, result); + ASSERT_ARE_EQUAL(size_t, 2, g_cached_ptr_array->_pointers.size()); + + ///cleanup + GIO_Async_Seq_Destroy(handle); + } + +END_TEST_SUITE(gio_async_seq_unittests) diff --git a/modules/ble/tests/gio_async_seq_unittests/main.c b/modules/ble/tests/gio_async_seq_unittests/main.c new file mode 100644 index 00000000..ad680491 --- /dev/null +++ b/modules/ble/tests/gio_async_seq_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(gio_async_seq_unittests, failedTestCount); + return failedTestCount; +} diff --git a/modules/common/messageproperties.h b/modules/common/messageproperties.h new file mode 100644 index 00000000..0060fba6 --- /dev/null +++ b/modules/common/messageproperties.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef MESSAGEPROPERTIES_H +#define MESSAGEPROPERTIES_H + +#define GW_MAC_ADDRESS_PROPERTY "macAddress" +#define GW_SOURCE_PROPERTY "source" +#define GW_DEVICENAME_PROPERTY "deviceName" +#define GW_DEVICEKEY_PROPERTY "deviceKey" + +#define GW_SOURCE_BLE_COMMAND "bleCommand" +#define GW_SOURCE_BLE_TELEMETRY "bleTelemetry" + +#define GW_IDMAP_MODULE "mapping" +#define GW_IOTHUB_MODULE "IoTHubHTTP" + +#define GW_BLE_CONTROLLER_INDEX_PROPERTY "bleControllerIndex" +#define GW_TIMESTAMP_PROPERTY "timestamp" +#define GW_CHARACTERISTIC_UUID_PROPERTY "characteristicUUID" + +#endif /*MESSAGEPROPERTIES_H*/ diff --git a/modules/hello_world/CMakeLists.txt b/modules/hello_world/CMakeLists.txt new file mode 100644 index 00000000..b3c6f12b --- /dev/null +++ b/modules/hello_world/CMakeLists.txt @@ -0,0 +1,71 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for the hello_world module +cmake_minimum_required(VERSION 2.8.11) + +set(hello_world_sources + ./src/hello_world.c +) + +set(hello_world_headers + ./inc/hello_world.h +) + +set(hello_world_static_sources + ${hello_world_sources} +) + +set(hello_world_static_headers + ${hello_world_headers} +) + +set(hello_world_hl_sources + ./src/hello_world_hl.c +) + +set(hello_world_hl_headers + ./inc/hello_world_hl.h +) + +set(hello_world_hl_static_sources + ${hello_world_hl_sources} +) + +set(hello_world_hl_static_headers + ${hello_world_hl_headers} +) + + +include_directories(./inc) +include_directories(${GW_INC}) + +#this builds the hello_world dynamic library +add_library(hello_world MODULE ${hello_world_sources} ${hello_world_headers}) +target_link_libraries(hello_world gateway) + +#this builds the hello_world static library +add_library(hello_world_static ${hello_world_static_sources} ${hello_world_static_headers}) +target_compile_definitions(hello_world_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(hello_world_static gateway) + +#this builds the hello_world dynamic library (by default it uses hello_world module linked statically) +add_library(hello_world_hl MODULE ${hello_world_hl_sources} ${hello_world_hl_headers}) +target_link_libraries(hello_world_hl hello_world_static gateway) + +#this builds the hello_world static library (by default it uses hello_world module linked statically) +add_library(hello_world_hl_static ${hello_world_hl_static_sources} ${hello_world_hl_static_headers}) +target_compile_definitions(hello_world_hl_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(hello_world_hl_static hello_world_static gateway) + +linkSharedUtil(hello_world) +linkSharedUtil(hello_world_static) +linkSharedUtil(hello_world_hl) +linkSharedUtil(hello_world_hl_static) + +add_module_to_solution(hello_world) +if(install_executables) + install(TARGETS hello_world LIBRARY DESTINATION lib) + install(TARGETS hello_world_hl LIBRARY DESTINATION lib) +endif() + diff --git a/modules/hello_world/README.md b/modules/hello_world/README.md new file mode 100644 index 00000000..df579102 --- /dev/null +++ b/modules/hello_world/README.md @@ -0,0 +1 @@ +# HelloWorld Module \ No newline at end of file diff --git a/modules/hello_world/inc/hello_world.h b/modules/hello_world/inc/hello_world.h new file mode 100644 index 00000000..665397ba --- /dev/null +++ b/modules/hello_world/inc/hello_world.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef HELLO_WORLD_H +#define HELLO_WORLD_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(HELLOWORLD_MODULE)(void); + +#ifdef __cplusplus +} +#endif + + +#endif /*HELLO_WORLD_H*/ diff --git a/modules/hello_world/inc/hello_world_hl.h b/modules/hello_world/inc/hello_world_hl.h new file mode 100644 index 00000000..dbc0aaec --- /dev/null +++ b/modules/hello_world/inc/hello_world_hl.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef HELLO_WORLD_HL_H +#define HELLO_WORLD_HL_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(HELLOWORLD_HL_MODULE)(void); + +#ifdef __cplusplus +} +#endif + + +#endif /*HELLO_WORLD_HL_H*/ diff --git a/modules/hello_world/src/hello_world.c b/modules/hello_world/src/hello_world.c new file mode 100644 index 00000000..9ff06910 --- /dev/null +++ b/modules/hello_world/src/hello_world.c @@ -0,0 +1,177 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "module.h" +#include "azure_c_shared_utility/iot_logging.h" + +#include "azure_c_shared_utility/threadapi.h" +#include "hello_world.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/lock.h" + +typedef struct HELLOWORLD_HANDLE_DATA_TAG +{ + THREAD_HANDLE threadHandle; + LOCK_HANDLE lockHandle; + int stopThread; + MESSAGE_BUS_HANDLE busHandle; + +}HELLOWORLD_HANDLE_DATA; + +#define HELLOWORLD_MESSAGE "hello world" + +int helloWorldThread(void *param) +{ + HELLOWORLD_HANDLE_DATA* handleData = param; + + MESSAGE_CONFIG msgConfig; + MAP_HANDLE propertiesMap = Map_Create(NULL); + if(propertiesMap == NULL) + { + LogError("unable to create a Map"); + } + else + { + if (Map_AddOrUpdate(propertiesMap, "helloWorld", "from Azure IoT Gateway SDK simple sample!") != MAP_OK) + { + LogError("unable to Map_AddOrUpdate"); + } + else + { + msgConfig.size = strlen(HELLOWORLD_MESSAGE); + msgConfig.source = HELLOWORLD_MESSAGE; + + msgConfig.sourceProperties = propertiesMap; + + MESSAGE_HANDLE helloWorldMessage = Message_Create(&msgConfig); + if (helloWorldMessage == NULL) + { + LogError("unable to create \"hello world\" message"); + } + else + { + while (1) + { + if (Lock(handleData->lockHandle) == LOCK_OK) + { + if (handleData->stopThread) + { + (void)Unlock(handleData->lockHandle); + break; /*gets out of the thread*/ + } + else + { + (void)MessageBus_Publish(handleData->busHandle, helloWorldMessage); + (void)Unlock(handleData->lockHandle); + } + } + else + { + /*shall retry*/ + } + (void)ThreadAPI_Sleep(5000); /*every 5 seconds*/ + } + Message_Destroy(helloWorldMessage); + } + } + } + return 0; +} + +static MODULE_HANDLE HelloWorld_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + HELLOWORLD_HANDLE_DATA* result; + if ( + (busHandle == NULL) /*configuration is not used*/ + ) + { + LogError("invalid arg busHandle=%p", busHandle); + result = NULL; + } + else + { + result = malloc(sizeof(HELLOWORLD_HANDLE_DATA)); + if(result == NULL) + { + LogError("unable to malloc"); + } + else + { + result->lockHandle = Lock_Init(); + if(result->lockHandle == NULL) + { + LogError("unable to Lock_Init"); + free(result); + result = NULL; + } + else + { + result->stopThread = 0; + result->busHandle = busHandle; + if (ThreadAPI_Create(&result->threadHandle, helloWorldThread, result) != THREADAPI_OK) + { + LogError("failed to spawn a thread"); + (void)Lock_Deinit(result->lockHandle); + free(result); + result = NULL; + } + else + { + /*all is fine apparently*/ + } + } + } + } + return result; +} + +static void HelloWorld_Destroy(MODULE_HANDLE module) +{ + /*first stop the thread*/ + HELLOWORLD_HANDLE_DATA* handleData = module; + int notUsed; + if (Lock(handleData->lockHandle) != LOCK_OK) + { + LogError("not able to Lock, still setting the thread to finish"); + handleData->stopThread = 1; + } + else + { + handleData->stopThread = 1; + Unlock(handleData->lockHandle); + } + + if(ThreadAPI_Join(handleData->threadHandle, ¬Used) != THREADAPI_OK) + { + LogError("unable to ThreadAPI_Join, still proceeding in _Destroy"); + } + + (void)Lock_Deinit(handleData->lockHandle); + free(handleData); +} + +static void HelloWorld_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + /*no action, HelloWorld is not interested in any messages*/ +} + +static const MODULE_APIS HelloWorld_APIS_all = +{ + HelloWorld_Create, + HelloWorld_Destroy, + HelloWorld_Receive +}; + +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(HELLOWORLD_MODULE)(void) +#else +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + return &HelloWorld_APIS_all; +} diff --git a/modules/hello_world/src/hello_world_hl.c b/modules/hello_world/src/hello_world_hl.c new file mode 100644 index 00000000..4c7adcf9 --- /dev/null +++ b/modules/hello_world/src/hello_world_hl.c @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "module.h" +#include "azure_c_shared_utility/iot_logging.h" +#include +#include "hello_world.h" +#include "hello_world_hl.h" + +static MODULE_HANDLE HelloWorld_HL_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + MODULE_HANDLE result; + if((result = MODULE_STATIC_GETAPIS(HELLOWORLD_MODULE)()->Module_Create(busHandle, configuration))==NULL) + { + LogError("unable to Module_Create HELLOWORLD static"); + } + else + { + /*all is fine, return as is*/ + } + return result; +} + +static void HelloWorld_HL_Destroy(MODULE_HANDLE module) +{ + MODULE_STATIC_GETAPIS(HELLOWORLD_MODULE)()->Module_Destroy(module); +} + +static void HelloWorld_HL_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + MODULE_STATIC_GETAPIS(HELLOWORLD_MODULE)()->Module_Receive(moduleHandle, messageHandle); +} + +static const MODULE_APIS HelloWorld_HL_APIS_all = +{ + HelloWorld_HL_Create, + HelloWorld_HL_Destroy, + HelloWorld_HL_Receive +}; + +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(HELLOWORLD_HL_MODULE)(void) +#else +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + return &HelloWorld_HL_APIS_all; +} diff --git a/modules/identitymap/CMakeLists.txt b/modules/identitymap/CMakeLists.txt new file mode 100644 index 00000000..40aace6b --- /dev/null +++ b/modules/identitymap/CMakeLists.txt @@ -0,0 +1,72 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) +#this is CMakeLists for the identitymap module + +set(identity_map_sources + ./src/identitymap.c +) + +set(identity_map_headers + ./inc/identitymap.h +) + +set(identity_map_static_sources + ${identity_map_sources} +) + +set(identity_map_static_headers + ${identity_map_headers} +) + +set(identity_map_hl_sources + ./src/identitymap_hl.c +) + +set(identity_map_hl_headers + ./inc/identitymap_hl.h +) + +set(identity_map_hl_static_sources + ${identity_map_hl_sources} +) + +set(identity_map_hl_static_headers + ${identity_map_hl_headers} +) + +include_directories(./inc) +include_directories(${GW_INC}) + +#this builds the identity_map dynamic library +add_library(identity_map MODULE ${identity_map_sources} ${identity_map_headers}) +target_link_libraries(identity_map gateway) + +#this builds the identity_map static library +add_library(identity_map_static ${identity_map_static_sources} ${identity_map_static_headers}) +target_compile_definitions(identity_map_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(identity_map_static gateway) + +#this builds the identity_map hl dynamic library (by default it uses identity_map module linked statically) +add_library(identity_map_hl MODULE ${identity_map_hl_sources} ${identity_map_hl_headers}) +target_link_libraries(identity_map_hl identity_map_static gateway) + +#this builds the identity_map hl static library (by default it uses identity_map module linked statically) +add_library(identity_map_hl_static ${identity_map_hl_static_sources} ${identity_map_hl_static_headers}) +target_compile_definitions(identity_map_hl_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(identity_map_hl_static identity_map_static gateway) + +linkSharedUtil(identity_map) +linkSharedUtil(identity_map_static) +linkSharedUtil(identity_map_hl) +linkSharedUtil(identity_map_hl_static) + +add_module_to_solution(identity_map) + +if(install_executables) + install(TARGETS identity_map LIBRARY DESTINATION lib) + install(TARGETS identity_map_hl LIBRARY DESTINATION lib) +endif() + +add_subdirectory(tests) \ No newline at end of file diff --git a/modules/identitymap/README.md b/modules/identitymap/README.md new file mode 100644 index 00000000..cf3719ee --- /dev/null +++ b/modules/identitymap/README.md @@ -0,0 +1 @@ +# Identity Map Module \ No newline at end of file diff --git a/modules/identitymap/devdoc/identity_map.md b/modules/identitymap/devdoc/identity_map.md new file mode 100644 index 00000000..b3b1a317 --- /dev/null +++ b/modules/identitymap/devdoc/identity_map.md @@ -0,0 +1,205 @@ +# Identity Map Module Requirements + +##Overview +This document describes the identity map module. This module maps MAC addresses +to device id and keys, and device ids to MAC Addresses. This module is +not multi-threaded, all work will be completed in the Receive callback. + +#### MAC Address to device name (Device to Cloud) +The module identifies the messages that it needs to process by the following +properties that must exist: + +>| PropertyName | Description | +>|--------------|------------------------------------------------------------------------------| +>| macAddress | The MAC address of a sensor, in canonical form | + +*and* if any of the following *do not exist*. + +>| PropertyName | Description | +>|--------------|--------------------------------------------------------------------------------| +>| deviceName | The deviceName as registered with IoTHub | +>| deviceKey | The key as registered with IoTHub | +>| source | Set to "mapping" - this shall ensure the mapping module rejects its own messages | + +When this module publishes a message, each message will have the the following properties: +>| PropertyName | Description | +>|--------------|------------------------------------------------------------------------------| +>| source | Source will be set to "mapping". | +>| deviceName | The deviceName as registered with IoTHub | +>| deviceKey | The key as registered with IoTHub | + +#### Device Id to MAC Address (Cloud to Device) + +The module identifies the messages that it needs to process by the following properties that must exist: +>| PropertyName | Description | +>|--------------|------------------------------------------------------------------------------| +>| deviceName | The deviceName as registered with IoTHub | +>| source | Set to "IoTHubHttp" | + +When this module publishes a message, each message will have the following proporties: +>| PropertyName | Description | +>|--------------|------------------------------------------------------------------------------| +>| source | Source will be set to "mapping". | +>| macAddress | The MAC address of a sensor, in canonical form | + + +The MAC addresses are expected in canonical form. For the purposes of this module, +the canonical form is "XX:XX:XX:XX:XX:XX", a sequence of six two-digit hexadecimal +numbers separated by colons, ie "AC:DE:48:12:7B:80". The MAC address is +case-insenstive. + +##References + +[module.h](../../../../devdoc/module.md) + +[vector.h](../../../../azure-c-shared-utility/c/inc/vector.h) + +IEEE Std 802-2014, section 8.1 (MAC Address canonical form) + +##Exposed API +```c + +typedef struct IDENTITY_MAP_CONFIG_TAG +{ + const char* macAddress; + const char* deviceId; + const char* deviceKey; +} IDENTITY_MAP_CONFIG; + +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void); + +``` + +## Module_GetAPIs + +This is the primary public interface for the module. It returns a pointer to +the `MODULE_APIS` structure containing the implementation functions for this module. +The following functions are the implementation of those APIs. + +**SRS_IDMAP_17_001 [** `Module_GetAPIs` shall return a non-`NULL` pointer to a MODULE_APIS structure.**]** **SRS_IDMAP_17_002: [**The `MODULE_APIS` structure shall have non-`NULL` `Module_Create`, `Module_Destroy`, and `Module_Receive` fields.**]** + +##IdentityMap_Create +```C +MODULE_HANDLE IdentityMap_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +``` + +This function creates the identity map module. This module expects a `VECTOR_HANDLE` +of `IDENTITY_MAP_CONFIG`, which contains a triplet of canonical form MAC +address, device ID and device key. The MAC address will be treated as the key for the MAC address to device array, and the deviceName will be treated as the key for the device to MAC address array. + +**SRS_IDMAP_17_003: [**Upon success, this function shall return a valid pointer to a `MODULE_HANDLE`.**]** +**SRS_IDMAP_17_004: [**If the `busHandle` is `NULL`, this function shall fail and return `NULL`.**]** +**SRS_IDMAP_17_005: [**If the configuration is `NULL`, this function shall fail and return `NULL`.**]** +**SRS_IDMAP_17_041: [**If the configuration has no vector elements, this function shall fail and return `NULL`.**]** +**SRS_IDMAP_17_019: [**If any `macAddress`, `deviceId` or `deviceKey` are `NULL`, this function shall fail and return `NULL`.**]** +**SRS_IDMAP_17_006: [**If any `macAddress` string in configuration is **not** a MAC address in canonical form, this function shall fail and return `NULL`.**]** + +Note that this module does not confirm the device ID and key are valid to IoT Hub. + +The valid module handle will be a pointer to the structure: + +```C +typedef struct IDENTITY_MAP_DATA_TAG +{ + MESSAGE_BUS_HANDLE busHandle; + size_t mappingSize; + IDENTITY_MAP_CONFIG * macToDeviceArray; + IDENTITY_MAP_CONFIG * deviceToMacArray; +} IDENTITY_MAP_DATA; +``` + +Where `busHandle` is the message bus passed in as input, `mappingSize` is the number of +elements in the vector `configuration`, and the `macToDeviceArray` and `deviceToMacArray` are the sorted lists +of mapping triplets. + +**SRS_IDMAP_17_010: [**If `IdentityMap_Create` fails to allocate a new `IDENTITY_MAP_DATA` structure, then this function shall fail, and return `NULL`.**]** +**SRS_IDMAP_17_011: [**If `IdentityMap_Create` fails to create memory for the macToDeviceArray, then this function shall fail and return `NULL`.**]** +**SRS_IDMAP_17_042: [** If `IdentityMap_Create` fails to create memory for the deviceToMacArray, then this function shall fail and return `NULL`. **]** +**SRS_IDMAP_17_012: [**If `IdentityMap_Create` fails to add a MAC address triplet to the macToDeviceArray, then this function shall fail, release all resources, and return `NULL`.**]** +**SRS_IDMAP_17_043: [** If `IdentityMap_Create` fails to add a MAC address triplet to the deviceToMacArray, then this function shall fail, release all resources, and return `NULL`. **]** + + +##Module_Destroy +```C +static void IdentityMap_Destroy(MODULE_HANDLE moduleHandle); +``` + +This function released all resources owned by the module specified by the `moduleHandle`. + +**SRS_IDMAP_17_018: [**If `moduleHandle` is `NULL`, `IdentityMap_Destroy` shall return.**]** +**SRS_IDMAP_17_015: [**`IdentityMap_Destroy` shall release all resources allocated for the module.**]** + + + +##IdentityMap_Receive +```C +static void IdentityMap_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); +``` + +This function will be the main work of this module. The processing of each +message in pseudocode is as follows: + +``` +01: If message properties contain a "macAddress" key and does not contain "source"=="mapping", or both "deviceName" and "deviceKey" keys, +02: Get MAC address from message properties via the "macAddress" key +03: Search macToDeviceArray for MAC address +04: If found, there is a new message to publish +05: Get deviceId and deviceKey from macToDeviceArray. +06: Create a new MAP from message properties. +07: Add or replace "deviceName" with deviceId +08: Add or replace "deviceKey" with deviceKey +09: Add or replace "source". +10: Else if message properties contain a "deviceName" key and does not contain "source"=="mapping" key, +11: Get deviceId from messages properties via the "deviceName" key +12: Search deviceToMacArray for deviceId +13: If found, there is a new message to publish +14: Get MAC address from deviceToMacArray +15: Create a new MAP from message properties. +16: Add or replace "macAddress" with MAC address. +17: Replace "source". +18: If there is a new message to publish, +19: Clone original mesage content. +20: Create a new message from MAP and original message content. +21: Publish new message on busHandle +22: Destroy all resources created +``` + +**SRS_IDMAP_17_020: [**If `moduleHandle` or `messageHandle` is `NULL`, then the function shall return.**]** +#### MAC Address to device name (D2C) +**SRS_IDMAP_17_021: [**If `messageHandle` properties does not contain "macAddress" property, then the message shall not be marked as a D2C message.**]** +**SRS_IDMAP_17_024: [**If `messageHandle` properties contains properties "deviceName" **and** "deviceKey", then the message shall not be marked as a D2C message.**]** +**SRS_IDMAP_17_044: [** If messageHandle properties contains a "source" property that is set to "mapping", the message shall not be marked as a D2C message. **]** +**SRS_IDMAP_17_040: [**If the `macAddress` of the message is not in canonical form, the message shall not be marked as a D2C message.**]** +**SRS_IDMAP_17_025: [**If the `macAddress` of the message is not found in the `macToDeviceArray` list, the message shall not be marked as a D2C message.**]** +On a message which passes all checks, the message shall be marked as a D2C message. +**SRS_IDMAP_17_026: [**On a D2C message received, `IdentityMap_Receive` shall call `ConstMap_CloneWriteable` on the message properties.**]** +**SRS_IDMAP_17_027: [**If `ConstMap_CloneWriteable` fails, `IdentityMap_Receive` shall deallocate any resources and return.**]** +Upon recognition of a D2C message, the following transformations will be done to create a message to send: +**SRS_IDMAP_17_028: [**`IdentityMap_Receive` shall call `Map_AddOrUpdate` with key of "deviceName" and value of found `deviceId`.**]** +**SRS_IDMAP_17_029: [**If adding `deviceName` fails,`IdentityMap_Receive` shall deallocate all resources and return.**]** +**SRS_IDMAP_17_030: [**`IdentityMap_Receive` shall call `Map_AddOrUpdate` with key of "deviceKey" and value of found `deviceKey`.**]** +**SRS_IDMAP_17_031: [**If adding `deviceKey` fails, `IdentityMap_Receive` shall deallocate all resources and return.**]** + +#### Device Id to MAC Address (C2D) +**SRS_IDMAP_17_045: [** If `messageHandle` properties does not contain "deviceName" property, then the message shall not be marked as a C2D message. **]** +**SRS_IDMAP_17_046: [** If messageHandle properties does not contain a "source" property, then the message shall not be marked as a C2D message. **]** +**SRS_IDMAP_17_047: [** If messageHandle property "source" is not equal to "IoTHubHttp", then the message shall not be marked as a C2D message. **]** +**SRS_IDMAP_17_048: [** If the `deviceName` of the message is not found in deviceToMacArray, then the message shall not be marked as a C2D message. **]** +On a message which passes all these checks, the message will be marked as a C2D message. +**SRS_IDMAP_17_049: [** On a C2D message received, `IdentityMap_Receive` shall call `ConstMap_CloneWriteable` on the message properties. **]** +**SRS_IDMAP_17_050: [** If `ConstMap_CloneWriteable` fails, `IdentityMap_Receive` shall deallocate any resources and return. **]** +Upon recognition of a C2D message, the following transformations will be done to create a message to send: +**SRS_IDMAP_17_051: [** `IdentityMap_Receive` shall call `Map_AddOrUpdate` with key of "macAddress" and value of found `macAddress`. **]** +**SRS_IDMAP_17_052: [** If adding `macAddress` fails, `IdentityMap_Receive` shall deallocate all resources and return. **]** + +#### Message to send exists +Upon recognition of a C2D or D2C message, then a new message shall be published. + +**SRS_IDMAP_17_032: [**`IdentityMap_Receive` shall call `Map_AddOrUpdate` with key of "source" and value of "mapping".**]** +**SRS_IDMAP_17_033: [**If adding source fails, `IdentityMap_Receive` shall deallocate all resources and return.**]** +**SRS_IDMAP_17_034: [**`IdentityMap_Receive` shall clone message content.**]** +**SRS_IDMAP_17_035: [**If cloning message content fails, `IdentityMap_Receive` shall deallocate all resources and return.**]** +**SRS_IDMAP_17_036: [**`IdentityMap_Receive` shall create a new message by calling `Message_CreateFromBuffer` with new map and cloned content.**]** +**SRS_IDMAP_17_037: [**If creating new message fails, `IdentityMap_Receive` shall deallocate all resources and return.**]** +**SRS_IDMAP_17_038: [**`IdentityMap_Receive` shall call `MessageBus_Publish` with `busHandle` and new message.**]** +**SRS_IDMAP_17_039: [**`IdentityMap_Receive` will destroy all resources it created.**]** diff --git a/modules/identitymap/devdoc/identity_map_hl.md b/modules/identitymap/devdoc/identity_map_hl.md new file mode 100644 index 00000000..23bee6fe --- /dev/null +++ b/modules/identitymap/devdoc/identity_map_hl.md @@ -0,0 +1,144 @@ +# Identity Map Module HL Requirements + +## Overview +This document describes the identity map high level module. This module adapts +the existing lower layer identity map module for use with the Gateway HL library. +It is mostly a passthrough to the existing module, with a specialized create +function to interpret the serialized JSON module arguments. + +The identity map module maps MAC addresses to device ids and keys. + +## Reference + +[module.h](../../../../devdoc/module.md) + +[Identity Map Module](identity_map.md) + +[vector.h](../../../../azure-c-shared-utility/c/inc/vector.h) + +IEEE Std 802-2014, section 8.1 (MAC Address canonical form) + +### Expected Arguments + +The arguments to this module is a JSON array of the following object: +```json +{ + "macAddress" : "", + "deviceId" : "", + "deviceKey" : "" +} +``` +### Example Arguments +```json +[ + { + "macAddress" : "01:01:01:01:01:01", + "deviceId" : "sample-device1", + "deviceKey" : "" + }, + { + "macAddress" : "02:02:02:02:02:02", + "deviceId" : "sample-device2", + "deviceKey" : "" + } +] +``` + +##Exposed API +```c +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void); +``` + +## Module_GetAPIs + +This is the primary public interface for the module. It returns a pointer to +the `MODULE_APIS` structure containing the implementation functions for this +module. The following functions are the implementation of those APIs. + +**SRS_IDMAP_HL_17_001: [**`Module_GetAPIs` shall return a non-`NULL` pointer +to a MODULE_APIS structure.**]** + +**SRS_IDMAP_HL_17_002: [** The `MODULE_APIS` structure shall have non-`NULL` +`Module_Create`, `Module_Destroy`, and `Module_Receive` fields. **]** + +## IdentityMap_HL_Create +```C +MODULE_HANDLE IdentityMap_HL_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +``` +This function creates the identity map HL module. This module reads a JSON +array and converts this to a VECTOR of IDENTITY_MAP_CONFIG, as expected by +the identity map module. Finally, it calls the identity map module create +function. + +**SRS_IDMAP_HL_17_003: [** If `busHandle` is NULL then + `IdentityMap_HL_Create` shall fail and return NULL. **]** + +**SRS_IDMAP_HL_17_004: [** If `configuration` is NULL then + `IdentityMap_HL_Create` shall fail and return NULL. **]** + +**SRS_IDMAP_HL_17_005: [** If `configuration` is not a JSON array of +JSON objects, then `IdentityMap_HL_Create` shall fail and return NULL. **]** + +**SRS_IDMAP_HL_17_006: [** `IdentityMap_HL_Create` shall parse the +`configuration` as a JSON array of objects. **]** + +**SRS_IDMAP_HL_17_007: [** `IdentityMap_HL_Create` shall call +VECTOR_create to make the identity map module input vector. **]** + +**SRS_IDMAP_HL_17_019: [** If creating the vector fails, then +`IdentityMap_HL_Create` shall fail and return NULL. **]** + +**SRS_IDMAP_HL_17_008: [** `IdentityMap_HL_Create` shall walk +through each object of the array. **]** + +**SRS_IDMAP_HL_17_009: [** If the array object does not contain a value +named "macAddress" then `IdentityMap_HL_Create` shall fail and return +NULL. **]** + +**SRS_IDMAP_HL_17_010: [** If the array object does not contain a value +named "deviceId" then `IdentityMap_HL_Create` shall fail and return +NULL. **]** + +**SRS_IDMAP_HL_17_011: [** If the array object does not contain a value +named "deviceKey" then `IdentityMap_HL_Create` shall fail and return +NULL. **]** + +**SRS_IDMAP_HL_17_012: [** `IdentityMap_HL_Create` shall use +"macAddress", "deviceId", and "deviceKey" values as the fields for an +IDENTITY_MAP_CONFIG structure and call VECTOR_push_back to add this element +to the vector. **]** + +**SRS_IDMAP_HL_17_020: [** If pushing into the vector is not successful, +then `IdentityMap_HL_Create` shall fail and return NULL. **]** + +**SRS_IDMAP_HL_17_013: [** `IdentityMap_HL_Create` shall invoke +identity map module's create, passing in the message bus handle and the input vector. +**]** + +**SRS_IDMAP_HL_17_014: [** When the lower layer identity map module +create succeeds, `IdentityMap_HL_Create` shall succeed and return a +non-NULL value. **]** + +**SRS_IDMAP_HL_17_015: [** If the lower layer identity map module create +fails, `IdentityMap_HL_Create` shall fail and return NULL. **]** + +**SRS_IDMAP_HL_17_016: [** `IdentityMap_HL_Create` shall release +all data it allocated. **]** + + +## IdentityMap_HL_Destroy +```C +static void IdentityMap_HL_Destroy(MODULE_HANDLE moduleHandle); +``` + +**SRS_IDMAP_HL_17_017: [** `IdentityMap_HL_Destroy` shall free all +used resources. **]** + + +## IdentityMap_HL_Receive +```C +static void IdentityMap_HL_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); +``` + +**SRS_IDMAP_HL_17_018: [** `IdentityMap_HL_Receive` shall pass the +received parameters to the underlying identity map module receive function. **]** diff --git a/modules/identitymap/inc/identitymap.h b/modules/identitymap/inc/identitymap.h new file mode 100644 index 00000000..32e47d0b --- /dev/null +++ b/modules/identitymap/inc/identitymap.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IDENTITYMAP_H +#define IDENTITYMAP_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct IDENTITY_MAP_CONFIG_TAG +{ + const char* macAddress; + const char* deviceId; + const char* deviceKey; +} IDENTITY_MAP_CONFIG; + +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)(void); + +#ifdef __cplusplus +} +#endif + +#endif /*IDENTITYMAP_H*/ diff --git a/modules/identitymap/inc/identitymap_hl.h b/modules/identitymap/inc/identitymap_hl.h new file mode 100644 index 00000000..646bf10c --- /dev/null +++ b/modules/identitymap/inc/identitymap_hl.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IDENTITYMAP_HL_H +#define IDENTITYMAP_HL_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE_HL)(void); + +#ifdef __cplusplus +} +#endif + +#endif /*IDENTITYMAP_HL_H*/ diff --git a/modules/identitymap/src/identitymap.c b/modules/identitymap/src/identitymap.c new file mode 100644 index 00000000..bd8cd5bd --- /dev/null +++ b/modules/identitymap/src/identitymap.c @@ -0,0 +1,642 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include "azure_c_shared_utility/gballoc.h" + +#include +#include +#include + +#include "azure_c_shared_utility/crt_abstractions.h" +#include "messageproperties.h" +#include "message.h" +#include "identitymap.h" +#include "azure_c_shared_utility/constmap.h" +#include "azure_c_shared_utility/constbuffer.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/vector.h" + +typedef struct IDENTITY_MAP_DATA_TAG +{ + MESSAGE_BUS_HANDLE busHandle; + size_t mappingSize; + IDENTITY_MAP_CONFIG * macToDevIdArray; + IDENTITY_MAP_CONFIG * devIdToMacArray; +} IDENTITY_MAP_DATA; + +#define IDENTITYMAP_RESULT_VALUES \ + IDENTITYMAP_OK, \ + IDENTITYMAP_ERROR, \ + IDENTITYMAP_MEMORY + +DEFINE_ENUM(IDENTITYMAP_RESULT, IDENTITYMAP_RESULT_VALUES) + +DEFINE_ENUM_STRINGS(MESSAGE_BUS_RESULT, MESSAGE_BUS_RESULT_VALUES); + +/* + * @brief duplicate a string and convert to upper case. New string must be released + * no longer needed. + */ +static const char * IdentityMapConfig_ToUpperCase(const char * string) +{ + char * result; + int status; + status = mallocAndStrcpy_s(&result, string); + if (status != 0) // failure + { + result = NULL; + } + else + { + char * temp = result; + while (*temp) + { + *temp = toupper(*temp); + temp++; + } + } + + return result; +} + +/* + * @brief Duplicate and copy all fields in the IDENTITY_MAP_CONFIG struct. + * Forces MAC address to be all upper case (to guarantee comparisons are consistent). + */ +static IDENTITYMAP_RESULT IdentityMapConfig_CopyDeep(IDENTITY_MAP_CONFIG * dest, IDENTITY_MAP_CONFIG * source) +{ + IDENTITYMAP_RESULT result; + int status; + dest->macAddress = IdentityMapConfig_ToUpperCase(source->macAddress); + if (dest->macAddress == NULL) + { + LogError("Unable to allocate macAddress"); + result = IDENTITYMAP_MEMORY; + } + else + { + char * temp; + status = mallocAndStrcpy_s(&temp, source->deviceId); + if (status != 0) + { + LogError("Unable to allocate deviceId"); + free((void*)dest->macAddress); + dest->macAddress = NULL; + result = IDENTITYMAP_MEMORY; + } + else + { + dest->deviceId = temp; + status = mallocAndStrcpy_s(&temp, source->deviceKey); + if (status != 0) + { + LogError("Unable to allocate deviceKey"); + free((void*)dest->deviceId); + dest->deviceId = NULL; + free((void*)dest->macAddress); + dest->macAddress = NULL; + result = IDENTITYMAP_MEMORY; + } + else + { + dest->deviceKey = temp; + result = IDENTITYMAP_OK; + } + } + } + return result; +} + +/* + * @brief Free strings associated with IDENTITY_MAP_CONFIG structure + */ +static void IdentityMapConfig_Free(IDENTITY_MAP_CONFIG * element) +{ + /*Codes_SRS_IDMAP_17_015: [IdentityMap_Destroy shall release all resources allocated for the module.]*/ + free((void*)element->macAddress); + free((void*)element->deviceId); + free((void*)element->deviceKey); +} + +/*Codes_SRS_IDMAP_17_006: [If any macAddress string in configuration is not a MAC address in canonical form, this function shall fail and return NULL.]*/ +static bool IdentityMapConfig_IsCanonicalMAC(const char * macAddress) +{ + /* Every MAC address must be in the form "XX:XX:XX:XX:XX:XX" X=[0-9,a-f,A-F] */ + bool recognized; + const size_t fixedSize = 17; + const size_t numDigits = 12; + const size_t numColons = 5; + const size_t digits[] = { 0, 1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16 }; + const size_t colons[] = { 2, 5, 8, 11, 14 }; + size_t macSize = strlen(macAddress); + if (macSize != fixedSize) + { + recognized = false; + } + else + { + recognized = true; + size_t i; + for (i = 0; i < numDigits; i++) + { + if (!isxdigit(macAddress[digits[i]])) + { + recognized = false; + break; + } + } + if (recognized == true) + { + for (i = 0; i < numColons; i++) + { + if (macAddress[colons[i]] != ':') + { + recognized = false; + break; + } + } + } + } + return recognized; +} + +/* + * @brief Comparison function by MAC address for two IDENTITY_MAP_CONFIG structures + */ +static int IdentityMapConfig_MacCompare(const void * a, const void * b) +{ + const IDENTITY_MAP_CONFIG * idA = a; + const IDENTITY_MAP_CONFIG * idB = b; + return strcmp(idA->macAddress, idB->macAddress); +} +/* +* @brief Comparison function by deviceId for two IDENTITY_MAP_CONFIG structures +*/ +static int IdentityMapConfig_IdCompare(const void * a, const void * b) +{ + const IDENTITY_MAP_CONFIG * idA = a; + const IDENTITY_MAP_CONFIG * idB = b; + return strcmp(idA->deviceId, idB->deviceId); +} + +/* + * @brief Walks through our mappingVector to ensure it is correct for our identity map module. + */ +static bool IdentityMap_ValidateConfig(const VECTOR_HANDLE mappingVector) +{ + size_t mappingSize = VECTOR_size(mappingVector); + bool mappingOk; + if (mappingSize == 0) + { + /*Codes_SRS_IDMAP_17_041: [If the configuration has no vector elements, this function shall fail and return NULL.*/ + LogError("nothing for this module to do, no mapping data"); + mappingOk = false; + } + else + { + mappingOk = true; + size_t index; + for (index = 0; (index < mappingSize) && (mappingOk != false); index++) + { + IDENTITY_MAP_CONFIG * element = (IDENTITY_MAP_CONFIG *)VECTOR_element(mappingVector, index); + if ((element->deviceId == NULL) || + (element->deviceKey == NULL) || + (element->macAddress == NULL)) + { + /*Codes_SRS_IDMAP_17_019: [If any macAddress, deviceId or deviceKey are NULL, this function shall fail and return NULL.]*/ + LogError("Empty mapping data values, mac=%p, ID=%p, key=%p", + element->macAddress, element->deviceId, element->deviceKey); + mappingOk = false; + break; + } + else + { + if (IdentityMapConfig_IsCanonicalMAC(element->macAddress) == false) + { + /*Codes_SRS_IDMAP_17_006: [If any macAddress string in configuration is not a MAC address in canonical form, this function shall fail and return NULL.]*/ + LogError("Non-canonical MAC Address: %s", element->macAddress); + mappingOk = false; + break; + } + } + } + } + return mappingOk; +} + +/* + * @brief Create an identity map module. + */ +static MODULE_HANDLE IdentityMap_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + IDENTITY_MAP_DATA* result; + if (busHandle == NULL || configuration == NULL) + { + /*Codes_SRS_IDMAP_17_004: [If the busHandle is NULL, this function shall fail and return NULL.]*/ + /*Codes_SRS_IDMAP_17_005: [If the configuration is NULL, this function shall fail and return NULL.]*/ + LogError("invalid parameter (NULL)."); + result = NULL; + } + else + { + VECTOR_HANDLE mappingVector = (VECTOR_HANDLE)configuration; + if (IdentityMap_ValidateConfig(mappingVector) == false) + { + LogError("unable to validate mapping table"); + result = NULL; + } + else + { + result = (IDENTITY_MAP_DATA*)malloc(sizeof(IDENTITY_MAP_DATA)); + if (result == NULL) + { + /*Codes_SRS_IDMAP_17_010: [If IdentityMap_Create fails to allocate a new IDENTITY_MAP_DATA structure, then this function shall fail, and return NULL.]*/ + LogError("Could not Allocate Module"); + } + else + { + size_t mappingSize = VECTOR_size(mappingVector); + /* validation ensures the vector is greater than zero */ + result->macToDevIdArray = (IDENTITY_MAP_CONFIG*)malloc(mappingSize*sizeof(IDENTITY_MAP_CONFIG)); + if (result->macToDevIdArray == NULL) + { + /*Codes_SRS_IDMAP_17_011: [If IdentityMap_Create fails to create memory for the macToDeviceArray, then this function shall fail and return NULL.*/ + LogError("Could not allocate mac to device mapping table"); + free(result); + result = NULL; + } + else + { + result->devIdToMacArray = (IDENTITY_MAP_CONFIG*)malloc(mappingSize*sizeof(IDENTITY_MAP_CONFIG)); + if (result->devIdToMacArray == NULL) + { + /*Codes_SRS_IDMAP_17_042: [ If IdentityMap_Create fails to create memory for the deviceToMacArray, then this function shall fail and return NULL. */ + LogError("Could not allocate devicee to mac mapping table"); + free(result->macToDevIdArray); + free(result); + result = NULL; + } + else + { + + size_t index; + size_t failureIndex = mappingSize; + for (index = 0; index < mappingSize; index++) + { + IDENTITY_MAP_CONFIG * element = (IDENTITY_MAP_CONFIG *)VECTOR_element(mappingVector, index); + IDENTITY_MAP_CONFIG * dest = &(result->macToDevIdArray[index]); + IDENTITYMAP_RESULT copyResult; + copyResult = IdentityMapConfig_CopyDeep(dest, element); + if (copyResult != IDENTITYMAP_OK) + { + failureIndex = index; + break; + } + dest = &(result->devIdToMacArray[index]); + copyResult = IdentityMapConfig_CopyDeep(dest, element); + if (copyResult != IDENTITYMAP_OK) + { + IdentityMapConfig_Free(&(result->macToDevIdArray[index])); + failureIndex = index; + break; + } + } + if (failureIndex < mappingSize) + { + /*Codes_SRS_IDMAP_17_012: [If IdentityMap_Create fails to add a MAC address triplet to the macToDeviceArray, then this function shall fail, release all resources, and return NULL.]*/ + /*Codes_SRS_IDMAP_17_043: [ If IdentityMap_Create fails to add a MAC address triplet to the deviceToMacArray, then this function shall fail, release all resources, and return NULL. */ + for (index = 0; index < failureIndex; index++) + { + IdentityMapConfig_Free(&(result->macToDevIdArray[index])); + IdentityMapConfig_Free(&(result->devIdToMacArray[index])); + } + free(result->macToDevIdArray); + free(result->devIdToMacArray); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IDMAP_17_003: [Upon success, this function shall return a valid pointer to a MODULE_HANDLE.]*/ + qsort(result->macToDevIdArray, mappingSize, sizeof(IDENTITY_MAP_CONFIG), + IdentityMapConfig_MacCompare); + qsort(result->devIdToMacArray, mappingSize, sizeof(IDENTITY_MAP_CONFIG), + IdentityMapConfig_IdCompare); + result->mappingSize = mappingSize; + result->busHandle = busHandle; + } + } + } + } + } + } + return result; +} + +/* +* @brief Destroy an identity map module. +*/ +static void IdentityMap_Destroy(MODULE_HANDLE moduleHandle) +{ + /*Codes_SRS_IDMAP_17_018: [If moduleHandle is NULL, IdentityMap_Destroy shall return.]*/ + if (moduleHandle != NULL) + { + /*Codes_SRS_IDMAP_17_015: [IdentityMap_Destroy shall release all resources allocated for the module.]*/ + IDENTITY_MAP_DATA * idModule = (IDENTITY_MAP_DATA*)moduleHandle; + for (size_t index = 0; index < idModule->mappingSize; index++) + { + IdentityMapConfig_Free(&(idModule->macToDevIdArray[index])); + IdentityMapConfig_Free(&(idModule->devIdToMacArray[index])); + } + free(idModule->macToDevIdArray); + free(idModule->devIdToMacArray); + free(idModule); + } +} + +static void publish_with_new_properties(MAP_HANDLE newProperties, MESSAGE_HANDLE messageHandle, IDENTITY_MAP_DATA * idModule) +{ + /*Codes_SRS_IDMAP_17_034: [IdentityMap_Receive shall clone message content.] */ + CONSTBUFFER_HANDLE content = Message_GetContentHandle(messageHandle); + if (content == NULL) + { + /*Codes_SRS_IDMAP_17_035: [If cloning message content fails, IdentityMap_Receive shall deallocate all resources and return.]*/ + LogError("Could not extract message content"); + } + else + { + MESSAGE_BUFFER_CONFIG newMessageConfig = + { + content, + newProperties + }; + /*Codes_SRS_IDMAP_17_036: [IdentityMap_Receive shall create a new message by calling Message_CreateFromBuffer with new map and cloned content.]*/ + MESSAGE_HANDLE newMessage = Message_CreateFromBuffer(&newMessageConfig); + if (newMessage == NULL) + { + /*Codes_SRS_IDMAP_17_037: [If creating new message fails, IdentityMap_Receive shall deallocate all resources and return.]*/ + LogError("Could not create new message to publish"); + } + else + { + MESSAGE_BUS_RESULT busStatus; + /*Codes_SRS_IDMAP_17_038: [IdentityMap_Receive shall call MessageBus_Publish with busHandle and new message.]*/ + busStatus = MessageBus_Publish(idModule->busHandle, newMessage); + if (busStatus != MESSAGE_BUS_OK) + { + LogError("Message bus publish failure: %s", ENUM_TO_STRING(MESSAGE_BUS_RESULT, busStatus)); + } + Message_Destroy(newMessage); + } + /*Codes_SRS_IDMAP_17_039: [IdentityMap_Receive will destroy all resources it created.]*/ + CONSTBUFFER_Destroy(content); + } +} + +/* + * @brief Republish message with new data from our matching identities. + */ +static void IdentityMap_RepublishD2C( + IDENTITY_MAP_DATA * idModule, + MESSAGE_HANDLE messageHandle, + IDENTITY_MAP_CONFIG * match) +{ + CONSTMAP_HANDLE properties = Message_GetProperties(messageHandle); + if (properties == NULL) + { + + LogError("Could not extract message properties"); + } + else + { + /*Codes_SRS_IDMAP_17_026: [On a message which passes all checks, IdentityMap_Receive shall call ConstMap_CloneWriteable on the message properties.]*/ + MAP_HANDLE newProperties = ConstMap_CloneWriteable(properties); + if (newProperties == NULL) + { + /*Codes_SRS_IDMAP_17_027: [If ConstMap_CloneWriteable fails, IdentityMap_Receive shall deallocate any resources and return.] */ + LogError("Could not make writeable new properties map"); + } + else + { + /*Codes_SRS_IDMAP_17_028: [IdentityMap_Receive shall call Map_AddOrUpdate with key of "deviceName" and value of found deviceId.]*/ + if (Map_AddOrUpdate(newProperties, GW_DEVICENAME_PROPERTY, match->deviceId) != MAP_OK) + { + /*Codes_SRS_IDMAP_17_029: [If adding deviceName fails,IdentityMap_Receive shall deallocate all resources and return.]*/ + LogError("Could not attach device name property to message"); + } + /*Codes_SRS_IDMAP_17_030: [IdentityMap_Receive shall call Map_AddOrUpdate with key of "deviceKey" and value of found deviceKey.]*/ + else if (Map_AddOrUpdate(newProperties, GW_DEVICEKEY_PROPERTY, match->deviceKey) != MAP_OK) + { + /*Codes_SRS_IDMAP_17_031: [If adding deviceKey fails, IdentityMap_Receive shall deallocate all resources and return.]*/ + LogError("Could not attach device key property to message"); + } + /*Codes_SRS_IDMAP_17_032: [IdentityMap_Receive shall call Map_AddOrUpdate with key of "source" and value of "mapping".]*/ + else if (Map_AddOrUpdate(newProperties, GW_SOURCE_PROPERTY, GW_IDMAP_MODULE) != MAP_OK) + { + /*Codes_SRS_IDMAP_17_033: [If adding source fails, IdentityMap_Receive shall deallocate all resources and return.]*/ + LogError("Could not attach source property to message"); + } + else + { + publish_with_new_properties(newProperties, messageHandle, idModule); + } + Map_Destroy(newProperties); + } + ConstMap_Destroy(properties); + } +} + +/* +* @brief Republish message with new data from our matching identities. +*/ +static void IdentityMap_RepublishC2D( + IDENTITY_MAP_DATA * idModule, + MESSAGE_HANDLE messageHandle, + IDENTITY_MAP_CONFIG * match) +{ + CONSTMAP_HANDLE properties = Message_GetProperties(messageHandle); + if (properties == NULL) + { + + LogError("Could not extract message properties"); + } + else + { + /*Codes_SRS_IDMAP_17_049: [ On a C2D message received, IdentityMap_Receive shall call ConstMap_CloneWriteable on the message properties. ]*/ + MAP_HANDLE newProperties = ConstMap_CloneWriteable(properties); + if (newProperties == NULL) + { + /*Codes_SRS_IDMAP_17_050: [ If ConstMap_CloneWriteable fails, IdentityMap_Receive shall deallocate any resources and return. ]*/ + LogError("Could not make writeable new properties map"); + } + else + { + /*Codes_SRS_IDMAP_17_051: [ IdentityMap_Receive shall call Map_AddOrUpdate with key of "macAddress" and value of found macAddress. ]*/ + if (Map_AddOrUpdate(newProperties, GW_MAC_ADDRESS_PROPERTY, match->macAddress) != MAP_OK) + { + /*Codes_SRS_IDMAP_17_052: [ If adding macAddress fails, IdentityMap_Receive shall deallocate all resources and return. ]*/ + LogError("Could not attach MAC address property to message"); + } + /*Codes_SRS_IDMAP_17_032: [IdentityMap_Receive shall call Map_AddOrUpdate with key of "source" and value of "mapping".]*/ + else if (Map_AddOrUpdate(newProperties, GW_SOURCE_PROPERTY, GW_IDMAP_MODULE) != MAP_OK) + { + /*Codes_SRS_IDMAP_17_033: [If adding source fails, IdentityMap_Receive shall deallocate all resources and return.]*/ + LogError("Could not attach source property to message"); + } + else + { + publish_with_new_properties(newProperties, messageHandle, idModule); + } + Map_Destroy(newProperties); + } + ConstMap_Destroy(properties); + } +} + +/* returns true if the message should continue to be processed, sets direction */ +static bool determine_message_direction(const char * source, bool * isC2DMessage) +{ + bool result; + if (source != NULL) + { + if (strcmp(source, GW_IOTHUB_MODULE) == 0) + { + result = true; + *isC2DMessage = true; + } + else if (strcmp(source, GW_IDMAP_MODULE) != 0) + { + result = true; + *isC2DMessage = false; + } + else + { + /*Codes_SRS_IDMAP_17_047: [ If messageHandle property "source" is not equal to "IoTHubHttp", then the message shall not be marked as a C2D message. ]*/ + /*Codes_SRS_IDMAP_17_044: [ If messageHandle properties contains a "source" property that is set to "mapping", the message shall not be marked as a D2C message. ]*/ + result = false; + *isC2DMessage = false; + } + } + else + { + /*Codes_SRS_IDMAP_17_046: [ If messageHandle properties does not contain a "source" property, then the message shall not be marked as a C2D message. ]*/ + result = false; + *isC2DMessage = false; + } + return result; +} + +/* + * @brief Receive a message from the message bus. + */ +static void IdentityMap_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + if (moduleHandle == NULL || messageHandle == NULL) + { + /*Codes_SRS_IDMAP_17_020: [If moduleHandle or messageHandle is NULL, then the function shall return.]*/ + LogError("Received NULL arguments: module = %p, massage = %p", moduleHandle, messageHandle); + } + else + { + IDENTITY_MAP_DATA * idModule = (IDENTITY_MAP_DATA*)moduleHandle; + + CONSTMAP_HANDLE properties = Message_GetProperties(messageHandle); + + const char * source = ConstMap_GetValue(properties, GW_SOURCE_PROPERTY); + bool isC2DMessage; + if (determine_message_direction(source, &isC2DMessage)) + { + if (isC2DMessage == true) + { + const char * deviceName = ConstMap_GetValue(properties, GW_DEVICENAME_PROPERTY); + /*Codes_SRS_IDMAP_17_045: [ If messageHandle properties does not contain "deviceName" property, then the message shall not be marked as a C2D message. */ + if (deviceName != NULL) + { + IDENTITY_MAP_CONFIG key = { NULL,deviceName,NULL }; + + IDENTITY_MAP_CONFIG * match = bsearch(&key, + idModule->devIdToMacArray, idModule->mappingSize, + sizeof(IDENTITY_MAP_CONFIG), + IdentityMapConfig_IdCompare); + if (match == NULL) + { + /*Codes_SRS_IDMAP_17_048: [ If the deviceName of the message is not found in deviceToMacArray, then the message shall not be marked as a C2D message. ]*/ + LogInfo("Did not find device Id [%s] of current message", deviceName); + } + else + { + IdentityMap_RepublishC2D(idModule, messageHandle, match); + } + } + } + else + { + const char * messageMac = IdentityMapConfig_ToUpperCase( + ConstMap_GetValue(properties, GW_MAC_ADDRESS_PROPERTY)); + + /*Codes_SRS_IDMAP_17_021: [If messageHandle properties does not contain "macAddress" property, then the function shall return.]*/ + if (messageMac != NULL) + { + /*Codes_SRS_IDMAP_17_024: [If messageHandle properties contains properties "deviceName" and "deviceKey", then this function shall return.] */ + if ((ConstMap_GetValue(properties, GW_DEVICENAME_PROPERTY) == NULL || + ConstMap_GetValue(properties, GW_DEVICEKEY_PROPERTY) == NULL)) + { + if (IdentityMapConfig_IsCanonicalMAC(messageMac) == false) + { + /*Codes_SRS_IDMAP_17_040: [If the macAddress of the message is not in canonical form, then this function shall return.]*/ + LogInfo("MAC address not valid: %s", messageMac); + } + else + { + IDENTITY_MAP_CONFIG key = { messageMac,NULL,NULL }; + + IDENTITY_MAP_CONFIG * match = bsearch(&key, + idModule->macToDevIdArray, idModule->mappingSize, + sizeof(IDENTITY_MAP_CONFIG), + IdentityMapConfig_MacCompare); + if (match == NULL) + { + /*Codes_SRS_IDMAP_17_025: [If the macAddress of the message is not found in the macToDeviceArray list, then this function shall return.]*/ + LogInfo("Did not find message MAC Address: %s", messageMac); + } + else + { + IdentityMap_RepublishD2C(idModule, messageHandle, match); + } + } + } + /*Codes_SRS_IDMAP_17_039: [IdentityMap_Receive will destroy all resources it created.]*/ + free((void*)messageMac); + } + } + } + ConstMap_Destroy(properties); + + } +} + + +/* + * Required for all modules: the public API and the designated implementation functions. + */ +static const MODULE_APIS IdentityMap_APIS_all = +{ + IdentityMap_Create, + IdentityMap_Destroy, + IdentityMap_Receive +}; + +/*Codes_SRS_IDMAP_17_001 [ Module_GetAPIs shall return a non-NULL pointer to a MODULE_APIS structure.]*/ +/*Codes_SRS_IDMAP_17_002: [The MODULE_APIS structure shall have non-NULL Module_Create, Module_Destroy, and Module_Receive fields.]*/ +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)(void) +#else +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + return &IdentityMap_APIS_all; +} diff --git a/modules/identitymap/src/identitymap_hl.c b/modules/identitymap/src/identitymap_hl.c new file mode 100644 index 00000000..d9871201 --- /dev/null +++ b/modules/identitymap/src/identitymap_hl.c @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/vector.h" +#include "message.h" +#include "parson.h" + +#include "identitymap.h" +#include "identitymap_hl.h" + +#define MACADDR "macAddress" +#define DEVICENAME "deviceId" +#define DEVICEKEY "deviceKey" + +static bool addOneRecord(VECTOR_HANDLE inputVector, JSON_Object * record) +{ + bool success; + if (record == NULL) + { + /*Codes_SRS_IDMAP_HL_17_005: [ If configuration is not a JSON array of JSON objects, then IdentityMap_HL_Create shall fail and return NULL. ]*/ + success = false; + } + else + { + const char * macAddress; + const char * deviceId; + const char * deviceKey; + if ((macAddress = json_object_get_string(record, MACADDR)) == NULL) + { + /*Codes_SRS_IDMAP_HL_17_009: [ If the array object does not contain a value named "macAddress" then IdentityMap_HL_Create shall fail and return NULL. ]*/ + LogError("Did not find expected %s configuration", MACADDR); + success = false; + } + else if ((deviceId = json_object_get_string(record, DEVICENAME)) == NULL) + { + /*Codes_SRS_IDMAP_HL_17_010: [ If the array object does not contain a value named "deviceId" then IdentityMap_HL_Create shall fail and return NULL. ]*/ + LogError("Did not find expected %s configuration", DEVICENAME); + success = false; + } + else if ((deviceKey = json_object_get_string(record, DEVICEKEY)) == NULL) + { + /*Codes_SRS_IDMAP_HL_17_011: [ If the array object does not contain a value named "deviceKey" then IdentityMap_HL_Create shall fail and return NULL. ]*/ + LogError("Did not find expected %s configuration", DEVICEKEY); + success = false; + } + else + { + /*Codes_SRS_IDMAP_HL_17_012: [ IdentityMap_HL_Create shall use "macAddress", "deviceId", and "deviceKey" values as the fields for an IDENTITY_MAP_CONFIG structure and call VECTOR_push_back to add this element to the vector. ]*/ + IDENTITY_MAP_CONFIG config; + config.macAddress = macAddress; + config.deviceId = deviceId; + config.deviceKey = deviceKey; + if (VECTOR_push_back(inputVector, &config, 1) != 0) + { + /*Codes_SRS_IDMAP_HL_17_020: [ If pushing into the vector is not successful, then IdentityMap_HL_Create shall fail and return NULL. ]*/ + LogError("Did not push vector"); + success = false; + } + else + { + success = true; + } + } + } + return success; +} + + +/* + * @brief Create an identity map HL module. + */ +static MODULE_HANDLE IdentityMap_HL_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + MODULE_HANDLE *result; + if ((busHandle == NULL) || (configuration == NULL)) + { + /*Codes_SRS_IDMAP_HL_17_003: [ If busHandle is NULL then IdentityMap_HL_Create shall fail and return NULL. ]*/ + /*Codes_SRS_IDMAP_HL_17_004: [ If configuration is NULL then IdentityMap_HL_Create shall fail and return NULL. ]*/ + LogError("Invalid NULL parameter, busHandle=[%p] configuration=[%p]", busHandle, configuration); + result = NULL; + } + else + { + /*Codes_SRS_IDMAP_HL_17_006: [ IdentityMap_HL_Create shall parse the configuration as a JSON array of objects. ]*/ + JSON_Value* json = json_parse_string((const char*)configuration); + if (json == NULL) + { + /*Codes_SRS_IDMAP_HL_17_005: [ If configuration is not a JSON array of JSON objects, then IdentityMap_HL_Create shall fail and return NULL. ]*/ + LogError("Unable to parse json string"); + result = NULL; + } + else + { + /*Codes_SRS_IDMAP_HL_17_006: [ IdentityMap_HL_Create shall parse the configuration as a JSON array of objects. ]*/ + JSON_Array* jsonArray = json_value_get_array(json); + if (jsonArray == NULL) + { + /*Codes_SRS_IDMAP_HL_17_005: [ If configuration is not a JSON array of JSON objects, then IdentityMap_HL_Create shall fail and return NULL. ]*/ + LogError("Expected a JSON Array in configuration"); + result = NULL; + } + else + { + /*Codes_SRS_IDMAP_HL_17_007: [ IdentityMap_HL_Create shall call VECTOR_create to make the identity map module input vector. ]*/ + VECTOR_HANDLE inputVector = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + if (inputVector == NULL) + { + /*Codes_SRS_IDMAP_HL_17_019: [ If creating the vector fails, then IdentityMap_HL_Create shall fail and return NULL. ]*/ + LogError("Failed to create the input vector"); + result = NULL; + } + else + { + size_t numberOfRecords = json_array_get_count(jsonArray); + size_t record; + bool arrayParsed = true; + /*Codes_SRS_IDMAP_HL_17_008: [ IdentityMap_HL_Create shall walk through each object of the array. ]*/ + for (record = 0; record < numberOfRecords; record++) + { + /*Codes_SRS_IDMAP_HL_17_006: [ IdentityMap_HL_Create shall parse the configuration as a JSON array of objects. ]*/ + if (addOneRecord(inputVector, json_array_get_object(jsonArray, record)) != true) + { + arrayParsed = false; + break; + } + } + if (arrayParsed != true) + { + /*Codes_SRS_IDMAP_HL_17_005: [ If configuration is not a JSON array of JSON objects, then IdentityMap_HL_Create shall fail and return NULL. ]*/ + result = NULL; + } + else + { + /*Codes_SRS_IDMAP_HL_17_013: [ IdentityMap_HL_Create shall invoke identity map module's create, passing in the message bus handle and the input vector. ]*/ + /*Codes_SRS_IDMAP_HL_17_014: [ When the lower layer identity map module create succeeds, IdentityMap_HL_Create shall succeed and return a non-NULL value. ]*/ + /*Codes_SRS_IDMAP_HL_17_015: [ If the lower layer identity map module create fails, IdentityMap_HL_Create shall fail and return NULL. ]*/ + result = MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)()->Module_Create(busHandle, inputVector); + } + /*Codes_SRS_IDMAP_HL_17_016: [ IdentityMap_HL_Create shall release all data it allocated. ]*/ + VECTOR_destroy(inputVector); + } + } + /*Codes_SRS_IDMAP_HL_17_016: [ IdentityMap_HL_Create shall release all data it allocated. ]*/ + json_value_free(json); + } + } + return result; +} + +/* +* @brief Destroy an identity map HL module. +*/ +static void IdentityMap_HL_Destroy(MODULE_HANDLE moduleHandle) +{ + /*Codes_SRS_IDMAP_HL_17_017: [ IdentityMap_HL_Destroy shall free all used resources. ]*/ + MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)()->Module_Destroy(moduleHandle); +} + + +/* + * @brief Receive a message from the message bus. + */ +static void IdentityMap_HL_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + /*Codes_SRS_IDMAP_HL_17_018: [ IdentityMap_HL_Receive shall pass the received parameters to the underlying identity map module receive function. ]*/ + MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)()->Module_Receive(moduleHandle, messageHandle); +} + + +/* + * Required for all modules: the public API and the designated implementation functions. + */ +/*Codes_SRS_IDMAP_HL_17_002: [ The MODULE_APIS structure shall have non-NULL Module_Create, Module_Destroy, and Module_Receive fields. ]*/ +static const MODULE_APIS IdentityMap_HL_APIS_all = +{ + IdentityMap_HL_Create, + IdentityMap_HL_Destroy, + IdentityMap_HL_Receive +}; + +/*Codes_SRS_IDMAP_HL_17_001: [Module_GetAPIs shall return a non-NULL pointer to a MODULE_APIS structure.]*/ +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE_HL)(void) +#else +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + return &IdentityMap_HL_APIS_all; +} diff --git a/modules/identitymap/tests/CMakeLists.txt b/modules/identitymap/tests/CMakeLists.txt new file mode 100644 index 00000000..d845e829 --- /dev/null +++ b/modules/identitymap/tests/CMakeLists.txt @@ -0,0 +1,9 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) +#this is CMakeLists for identity map tests + +add_subdirectory(idmap_unittests) +add_subdirectory(idmap_hl_unittests) + diff --git a/modules/identitymap/tests/idmap_hl_unittests/CMakeLists.txt b/modules/identitymap/tests/idmap_hl_unittests/CMakeLists.txt new file mode 100644 index 00000000..15b15db2 --- /dev/null +++ b/modules/identitymap/tests/idmap_hl_unittests/CMakeLists.txt @@ -0,0 +1,23 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for idmap_hl_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName identitymap_hl_unittests) +set(${theseTestsName}_cpp_files +${theseTestsName}.cpp +) + +set(${theseTestsName}_c_files + ../../src/identitymap_hl.c + +) + +set(${theseTestsName}_h_files +) + +include_directories(${GW_INC}) + +build_test_artifacts(${theseTestsName} ON) \ No newline at end of file diff --git a/modules/identitymap/tests/idmap_hl_unittests/identitymap_hl_unittests.cpp b/modules/identitymap/tests/idmap_hl_unittests/identitymap_hl_unittests.cpp new file mode 100644 index 00000000..2cfa753f --- /dev/null +++ b/modules/identitymap/tests/idmap_hl_unittests/identitymap_hl_unittests.cpp @@ -0,0 +1,803 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/vector.h" + +#include "parson.h" + +#include "identitymap.h" +#include "identitymap_hl.h" + + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +/*these are simple cached variables*/ +static pfModule_Create Module_Create = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Destroy Module_Destroy = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Receive Module_Receive = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +extern "C" int mallocAndStrcpy_s(char** destination, const char*source); +extern "C" int unsignedIntToString(char* destination, size_t destinationSize, unsigned int value); +extern "C" int size_tToString(char* destination, size_t destinationSize, size_t value); + + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit + +#include "vector.c" +}; + + +/*forward declarations*/ +static MODULE_HANDLE IdentityMap_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +/*this destroys (frees) resources of the module parameter*/ +static void IdentityMap_Destroy(MODULE_HANDLE moduleHandle); +/*this is the module's callback function - gets called when a message is to be received by the module*/ +static void IdentityMap_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); + +static const MODULE_APIS IoTHubModuleHttp_GetAPIS_Impl = +{ + IdentityMap_Create, + IdentityMap_Destroy, + IdentityMap_Receive +}; + +typedef struct json_value_t +{ + int fake; +} JSON_Value; + +typedef struct json_object_t +{ + int fake; +} JSON_Object; + + +TYPED_MOCK_CLASS(CIdentitymapHlMocks, CGlobalMock) + { + public: + + // IdentityMap LL mocks + MOCK_STATIC_METHOD_0(, const MODULE_APIS*, MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)) + MOCK_METHOD_END(const MODULE_APIS*, (const MODULE_APIS*)&IoTHubModuleHttp_GetAPIS_Impl); + + MOCK_STATIC_METHOD_2(, MODULE_HANDLE, IdentityMap_Create, MESSAGE_BUS_HANDLE, busHandle, const void*, configuration) + MOCK_METHOD_END(MODULE_HANDLE, malloc(1)); + + MOCK_STATIC_METHOD_1(, void, IdentityMap_Destroy, MODULE_HANDLE, moduleHandle) + free(moduleHandle); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_2(, void, IdentityMap_Receive, MODULE_HANDLE, moduleHandle, MESSAGE_HANDLE, messageHandle) + MOCK_VOID_METHOD_END(); + + // Parson Mocks + MOCK_STATIC_METHOD_1(, JSON_Value*, json_parse_string, const char *, parseString) + JSON_Value* value = NULL; + if (parseString != NULL) + { + value = (JSON_Value*)malloc(1); + } + MOCK_METHOD_END(JSON_Value*, value); + + MOCK_STATIC_METHOD_2(, JSON_Object *, json_array_get_object, const JSON_Array *, array, size_t, index) + JSON_Object* object = NULL; + if (array != NULL) + { + object = (JSON_Object*)0x42; + } + MOCK_METHOD_END(JSON_Object*, object); + + MOCK_STATIC_METHOD_1(, JSON_Array*, json_value_get_array, const JSON_Value*, value) + JSON_Array* object = NULL; + if (value != NULL) + { + object = (JSON_Array*)0x43; + } + MOCK_METHOD_END(JSON_Array*, object); + + MOCK_STATIC_METHOD_1(, size_t, json_array_get_count, const JSON_Array *, array) + MOCK_METHOD_END(size_t, (size_t)0); + + MOCK_STATIC_METHOD_2(, const char*, json_object_get_string, const JSON_Object*, object, const char*, name) + const char * result2; + if (strcmp(name, "macAddress") == 0) + { + result2 = "mac"; + } + else if (strcmp(name, "deviceId") == 0) + { + result2 = "id"; + } + else if (strcmp(name, "deviceKey") == 0) + { + result2 = "key"; + } + else + { + result2 = NULL; + } + MOCK_METHOD_END(const char*, result2); + + MOCK_STATIC_METHOD_1(, void, json_value_free, JSON_Value*, value) + free(value); + MOCK_VOID_METHOD_END(); + + + // vector.h + MOCK_STATIC_METHOD_1(, VECTOR_HANDLE, VECTOR_create, size_t, elementSize) + auto r = BASEIMPLEMENTATION::VECTOR_create(elementSize); + MOCK_METHOD_END(VECTOR_HANDLE, r) + + MOCK_STATIC_METHOD_1(, void, VECTOR_destroy, VECTOR_HANDLE, handle) + BASEIMPLEMENTATION::VECTOR_destroy(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements) + auto r = BASEIMPLEMENTATION::VECTOR_push_back( handle, elements, numElements); + MOCK_METHOD_END(int, r) + + MOCK_STATIC_METHOD_3(, void, VECTOR_erase, VECTOR_HANDLE, handle, void*, elements, size_t, numElements) + BASEIMPLEMENTATION::VECTOR_erase(handle, elements, numElements); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, void, VECTOR_clear, VECTOR_HANDLE, handle) + BASEIMPLEMENTATION::VECTOR_clear(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, void*, VECTOR_element, const VECTOR_HANDLE, handle, size_t, index) + void * result1; + result1 = BASEIMPLEMENTATION::VECTOR_element(handle, index); + MOCK_METHOD_END(void*, result1) + + MOCK_STATIC_METHOD_1(, void*, VECTOR_front, const VECTOR_HANDLE, handle) + auto r = BASEIMPLEMENTATION::VECTOR_front( handle); + MOCK_METHOD_END(void*, r) + + MOCK_STATIC_METHOD_1(, void*, VECTOR_back, const VECTOR_HANDLE, handle) + auto r = BASEIMPLEMENTATION::VECTOR_back( handle); + MOCK_METHOD_END(void*, r) + + MOCK_STATIC_METHOD_3(, void*, VECTOR_find_if, const VECTOR_HANDLE, handle, PREDICATE_FUNCTION, pred, const void*, value) + auto r = BASEIMPLEMENTATION::VECTOR_find_if( handle, pred, value); + MOCK_METHOD_END(void*, r) + + MOCK_STATIC_METHOD_1(, size_t, VECTOR_size, const VECTOR_HANDLE, handle) + auto r = BASEIMPLEMENTATION::VECTOR_size( handle); + MOCK_METHOD_END(size_t, r) + + }; + + +DECLARE_GLOBAL_MOCK_METHOD_0(CIdentitymapHlMocks, , const MODULE_APIS*, MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)); + +DECLARE_GLOBAL_MOCK_METHOD_2(CIdentitymapHlMocks, , MODULE_HANDLE, IdentityMap_Create, MESSAGE_BUS_HANDLE, busHandle, const void*, configuration); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapHlMocks, , void, IdentityMap_Destroy, MODULE_HANDLE, moduleHandle); +DECLARE_GLOBAL_MOCK_METHOD_2(CIdentitymapHlMocks, , void, IdentityMap_Receive, MODULE_HANDLE, moduleHandle, MESSAGE_HANDLE, messageHandle); + +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapHlMocks, , JSON_Value*, json_parse_string, const char *, filename); +DECLARE_GLOBAL_MOCK_METHOD_2(CIdentitymapHlMocks, , JSON_Object *, json_array_get_object, const JSON_Array *, array, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapHlMocks, , JSON_Array*, json_value_get_array, const JSON_Value*, value); +DECLARE_GLOBAL_MOCK_METHOD_2(CIdentitymapHlMocks, , const char*, json_object_get_string, const JSON_Object*, object, const char*, name); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapHlMocks, , size_t, json_array_get_count, const JSON_Array *, array); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapHlMocks, , void, json_value_free, JSON_Value*, value); + +// vector.h +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapHlMocks, , VECTOR_HANDLE, VECTOR_create, size_t, elementSize); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapHlMocks, , void, VECTOR_destroy, VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CIdentitymapHlMocks, , int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements); +DECLARE_GLOBAL_MOCK_METHOD_3(CIdentitymapHlMocks, , void, VECTOR_erase, VECTOR_HANDLE, handle, void*, elements, size_t, numElements); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapHlMocks, , void, VECTOR_clear, VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_2(CIdentitymapHlMocks, , void*, VECTOR_element, const VECTOR_HANDLE, handle, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapHlMocks, , void*, VECTOR_front, const VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapHlMocks, , void*, VECTOR_back, const VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CIdentitymapHlMocks, , void*, VECTOR_find_if, const VECTOR_HANDLE, handle, PREDICATE_FUNCTION, pred, const void*, value); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapHlMocks, , size_t, VECTOR_size, const VECTOR_HANDLE, handle); + + + +BEGIN_TEST_SUITE(identitymap_hl_unittests) + + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + + Module_Create = Module_GetAPIS()->Module_Create; + Module_Destroy = Module_GetAPIS()->Module_Destroy; + Module_Receive = Module_GetAPIS()->Module_Receive; + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + + } + + //Tests_SRS_IDMAP_HL_17_001: [Module_GetAPIs shall return a non-NULL pointer to a MODULE_APIS structure.] + //Tests_SRS_IDMAP_HL_17_002: [ The MODULE_APIS structure shall have non-NULL Module_Create, Module_Destroy, and Module_Receive fields. ] + TEST_FUNCTION(IdentityMap_HL_GetAPIs_Success) + { + ///Arrange + CIdentitymapHlMocks mocks; + + ///Act + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + ///Assert + ASSERT_IS_NOT_NULL(theAPIS); + ASSERT_IS_NOT_NULL((void*)theAPIS->Module_Create); + ASSERT_IS_NOT_NULL((void*)theAPIS->Module_Destroy); + ASSERT_IS_NOT_NULL((void*)theAPIS->Module_Receive); + + ///Ablution + } + + //Tests_SRS_IDMAP_HL_17_003: [ If busHandle is NULL then IdentityMap_HL_Create shall fail and return NULL. ] + TEST_FUNCTION(IdentityMap_HL_Create_Bus_Null) + { + ///Arrange + CIdentitymapHlMocks mocks; + MESSAGE_BUS_HANDLE bus = NULL; + unsigned char config; + + ///Act + auto n = Module_Create(bus, &config); + + ///Assert + ASSERT_IS_NULL(n); + + ///Ablution + } + + //Tests_SRS_IDMAP_HL_17_004: [ If configuration is NULL then IdentityMap_HL_Create shall fail and return NULL. ] + TEST_FUNCTION(IdentityMap_HL_Create_Config_Null) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + void * config = NULL; + + ///Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NULL(n); + + ///Ablution + } + + //Tests_SRS_IDMAP_HL_17_006: [ IdentityMap_HL_Create shall parse the configuration as a JSON array of objects. ] + //Tests_SRS_IDMAP_HL_17_007: [ IdentityMap_HL_Create shall call VECTOR_create to make the identity map module input vector. ] + //Tests_SRS_IDMAP_HL_17_008: [ IdentityMap_HL_Create shall walk through each object of the array. ] + //Tests_SRS_IDMAP_HL_17_012: [ IdentityMap_HL_Create shall use "macAddress", "deviceId", and "deviceKey" values as the fields for an IDENTITY_MAP_CONFIG structure and call VECTOR_push_back to add this element to the vector. ] + //Tests_SRS_IDMAP_HL_17_013: [ IdentityMap_HL_Create shall invoke identity map module's create, passing in the message bus handle and the input vector. ] + //Tests_SRS_IDMAP_HL_17_014: [ When the lower layer identity map module create succeeds, IdentityMap_HL_Create shall succeed and return a non-NULL value. ] + //Tests_SRS_IDMAP_HL_17_016: [ IdentityMap_HL_Create shall release all data it allocated. ] + TEST_FUNCTION(IdentityMap_HL_Create_Success) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + const char* config = "pretend this is a valid JSON string"; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(config)); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_array(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(IDENTITY_MAP_CONFIG))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "macAddress")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceId")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceKey")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)()); + STRICT_EXPECTED_CALL(mocks, IdentityMap_Create(bus, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + + //Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NOT_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Cleanup + + Module_Destroy(n); + } + + //Tests_SRS_IDMAP_HL_17_008: [ IdentityMap_HL_Create shall walk through each object of the array. ] + TEST_FUNCTION(IdentityMap_HL_Create_Success_with_2_element_array) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + const char* config = "pretend this is a valid JSON string"; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(config)); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_array(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(IDENTITY_MAP_CONFIG))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)2); + + { + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "macAddress")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceId")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceKey")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + } + { + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "macAddress")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceId")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceKey")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + } + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)()); + STRICT_EXPECTED_CALL(mocks, IdentityMap_Create(bus, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + + //Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NOT_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Cleanup + + Module_Destroy(n); + } + + //Tests_SRS_IDMAP_HL_17_015: [ If the lower layer identity map module create fails, IdentityMap_HL_Create shall fail and return NULL. ] + TEST_FUNCTION(IdentityMap_HL_Create_ll_Create_failed_returns_null) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + const char* config = "pretend this is a valid JSON string"; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(config)); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_array(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(IDENTITY_MAP_CONFIG))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "macAddress")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceId")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceKey")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)()); + STRICT_EXPECTED_CALL(mocks, IdentityMap_Create(bus, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .SetFailReturn((MODULE_HANDLE)NULL); + + //Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Cleanup + + } + + //Tests_SRS_IDMAP_HL_17_020: [ If pushing into the vector is not successful, then IdentityMap_HL_Create shall fail and return NULL. ] + TEST_FUNCTION(IdentityMap_HL_Create_push_back_failed_returns_null) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + const char* config = "pretend this is a valid JSON string"; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(config)); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_array(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(IDENTITY_MAP_CONFIG))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "macAddress")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceId")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceKey")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn(__LINE__); + + //Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Cleanup + } + + //Tests_SRS_IDMAP_HL_17_009: [ If the array object does not contain a value named "macAddress" then IdentityMap_HL_Create shall fail and return NULL. ] + TEST_FUNCTION(IdentityMap_HL_Create_no_key_returns_null) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + const char* config = "pretend this is a valid JSON string"; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(config)); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_array(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(IDENTITY_MAP_CONFIG))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "macAddress")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceId")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceKey")) + .IgnoreArgument(1) + .SetFailReturn((const char*)NULL); + + //Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Cleanup + } + + //Tests_SRS_IDMAP_HL_17_010: [ If the array object does not contain a value named "deviceId" then IdentityMap_HL_Create shall fail and return NULL. ] + TEST_FUNCTION(IdentityMap_HL_Create_no_id_returns_null) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + const char* config = "pretend this is a valid JSON string"; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(config)); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_array(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(IDENTITY_MAP_CONFIG))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "macAddress")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "deviceId")) + .IgnoreArgument(1) + .SetFailReturn((const char*)NULL); + + //Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Cleanup + } + + //Tests_SRS_IDMAP_HL_17_011: [ If the array object does not contain a value named "deviceKey" then IdentityMap_HL_Create shall fail and return NULL. ] + TEST_FUNCTION(IdentityMap_HL_Create_no_mac_returns_null) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + const char* config = "pretend this is a valid JSON string"; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(config)); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_array(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(IDENTITY_MAP_CONFIG))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "macAddress")) + .IgnoreArgument(1) + .SetFailReturn((const char*)NULL); + + //Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Cleanup + } + + //Tests_SRS_IDMAP_HL_17_005: [ If configuration is not a JSON array of JSON objects, then IdentityMap_HL_Create shall fail and return NULL. ] + TEST_FUNCTION(IdentityMap_HL_Create_no_object_returns_null) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + const char* config = "pretend this is a valid JSON string"; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(config)); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_array(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(IDENTITY_MAP_CONFIG))); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_array_get_count(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetReturn((size_t)1); + STRICT_EXPECTED_CALL(mocks, json_array_get_object(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn((JSON_Object*)NULL); + + //Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Cleanup + } + + //Tests_SRS_IDMAP_HL_17_019: [ If creating the vector fails, then IdentityMap_HL_Create shall fail and return NULL. ] + TEST_FUNCTION(IdentityMap_HL_Create_vector_create_returns_null) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + const char* config = "pretend this is a valid JSON string"; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(config)); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_array(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_create(sizeof(IDENTITY_MAP_CONFIG))) + .SetFailReturn((VECTOR_HANDLE)NULL); + + //Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Cleanup + } + + //Tests_SRS_IDMAP_HL_17_005: [ If configuration is not a JSON array of JSON objects, then IdentityMap_HL_Create shall fail and return NULL. ] + TEST_FUNCTION(IdentityMap_HL_Create_no_array_returns_null) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + const char* config = "pretend this is a valid JSON string"; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(config)); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_value_get_array(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((JSON_Array*)NULL); + + //Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Cleanup + } + + //Tests_SRS_IDMAP_HL_17_005: [ If configuration is not a JSON array of JSON objects, then IdentityMap_HL_Create shall fail and return NULL. ] + TEST_FUNCTION(IdentityMap_HL_Create_parse_fails_returns_null) + { + ///Arrange + CIdentitymapHlMocks mocks; + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + const char* config = "pretend this is a valid JSON string"; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(config)) + .SetFailReturn((JSON_Value*)NULL); + + //Act + auto n = Module_Create(bus, config); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Cleanup + } + + //Tests_SRS_IDMAP_HL_17_017: [ IdentityMap_HL_Destroy shall free all used resources. ] + TEST_FUNCTION(IdentityMap_HL_Destroy_does_everything) + { + ///arrange + CIdentitymapHlMocks mocks; + const char * validJsonString = "calling it valid makes it so"; + MESSAGE_BUS_HANDLE busHandle = (MESSAGE_BUS_HANDLE)0x42; + auto result = Module_Create(busHandle, validJsonString); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)()); + STRICT_EXPECTED_CALL(mocks, IdentityMap_Destroy(result)); + + ///act + Module_Destroy(result); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ///cleanup + } + + //Tests_SRS_IDMAP_HL_17_018: [ IdentityMap_HL_Receive shall pass the received parameters to the underlying identity map module receive function. ] + TEST_FUNCTION(IdentityMap_HL_Receive_does_everything) + { + ///arrange + CIdentitymapHlMocks mocks; + const char * validJsonString = "calling it valid makes it so"; + MESSAGE_BUS_HANDLE busHandle = (MESSAGE_BUS_HANDLE)0x42; + MESSAGE_HANDLE messageHandle = (MESSAGE_HANDLE)0x42; + auto result = Module_Create(busHandle, validJsonString); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(IDENTITYMAP_MODULE)()); + STRICT_EXPECTED_CALL(mocks, IdentityMap_Receive(result, messageHandle)); + + ///act + Module_Receive(result, messageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(result); + } + +END_TEST_SUITE(identitymap_hl_unittests) diff --git a/modules/identitymap/tests/idmap_hl_unittests/main.c b/modules/identitymap/tests/idmap_hl_unittests/main.c new file mode 100644 index 00000000..36b74eda --- /dev/null +++ b/modules/identitymap/tests/idmap_hl_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(identitymap_hl_unittests, failedTestCount); + return failedTestCount; +} diff --git a/modules/identitymap/tests/idmap_unittests/CMakeLists.txt b/modules/identitymap/tests/idmap_unittests/CMakeLists.txt new file mode 100644 index 00000000..22b68aa8 --- /dev/null +++ b/modules/identitymap/tests/idmap_unittests/CMakeLists.txt @@ -0,0 +1,23 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for idmap_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName identitymap_unittests) +set(${theseTestsName}_cpp_files +${theseTestsName}.cpp +) + +set(${theseTestsName}_c_files + ../../src/identitymap.c + +) + +set(${theseTestsName}_h_files +) + +include_directories(${GW_INC}) + +build_test_artifacts(${theseTestsName} ON) \ No newline at end of file diff --git a/modules/identitymap/tests/idmap_unittests/identitymap_unittests.cpp b/modules/identitymap/tests/idmap_unittests/identitymap_unittests.cpp new file mode 100644 index 00000000..6d09cdbd --- /dev/null +++ b/modules/identitymap/tests/idmap_unittests/identitymap_unittests.cpp @@ -0,0 +1,2939 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/vector.h" +#include "messageproperties.h" + + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +#define GBALLOC_H + +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +extern "C" int mallocAndStrcpy_s(char** destination, const char*source); +extern "C" int unsignedIntToString(char* destination, size_t destinationSize, unsigned int value); +extern "C" int size_tToString(char* destination, size_t destinationSize, size_t value); + + +namespace BASEIMPLEMENTATION +{ + /*if malloc is defined as gballoc_malloc at this moment, there'd be serious trouble*/ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit + +#include "vector.c" +}; + +#include "identitymap.h" +#include "azure_c_shared_utility/crt_abstractions.h" + +static size_t currentmalloc_call; +static size_t whenShallmalloc_fail; + +static size_t currentMap_call; +static size_t whenShallMap_fail; + +static size_t currentConstMap_Create_call; +static size_t whenShallConstMap_Create_fail; + +static size_t currentConstMap_Clone_call; +static size_t whenShallConstMap_Clone_fail; + +static size_t currentConstMap_CloneWriteable_call; +static size_t whenShallConstMap_CloneWriteable_fail; + +static size_t currentCONSTBUFFER_Create_call; +static size_t whenShallCONSTBUFFER_Create_fail; + +static size_t currentCONSTBUFFER_Clone_call; +static size_t whenShallCONSTBUFFER_Clone_fail; + +static size_t currentStrdup_call; +static size_t whenShallStrdup_fail; + +static size_t currentVectorElement_call; +static size_t whenShallVectorElement_fail; + +static size_t currentMessage_call; +static size_t whenShallMessage_fail; +static CONSTBUFFER messageContent; + +class RefCountObject +{ +private: + size_t ref_count; + +public: + RefCountObject() : ref_count(1) + { + } + + size_t inc_ref() + { + return ++ref_count; + } + + void dec_ref() + { + if (--ref_count == 0) + { + delete this; + } + } +}; + +#define VALID_MAP_HANDLE 0xDEAF +#define VALID_VALUE "value" +static MAP_RESULT currentMapResult; + +static MESSAGE_BUS_RESULT currentMessageBusResult; + +//CONSTMAP GetValue mocks +static const char* macAddressProperties; +static const char* sourceProperties; +static const char* deviceNameProperties; +static const char* deviceKeyProperties; + +static VECTOR_HANDLE testVector1; +static VECTOR_HANDLE testVector2; + +TYPED_MOCK_CLASS(CIdentitymapMocks, CGlobalMock) + { + public: + + // memory + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2; + currentmalloc_call++; + if (whenShallmalloc_fail>0) + { + if (currentmalloc_call == whenShallmalloc_fail) + { + result2 = NULL; + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + } + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + } + MOCK_METHOD_END(void*, result2) + + MOCK_STATIC_METHOD_2(, void*, gballoc_realloc, void*, ptr, size_t, size) + MOCK_METHOD_END(void*, BASEIMPLEMENTATION::gballoc_realloc(ptr, size)); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + + // MessageBus mocks + MOCK_STATIC_METHOD_0(, MESSAGE_BUS_HANDLE, MessageBus_Create) + MESSAGE_BUS_HANDLE busResult = (MESSAGE_BUS_HANDLE)(new RefCountObject()); + MOCK_METHOD_END(MESSAGE_BUS_HANDLE, busResult) + + MOCK_STATIC_METHOD_1(, void, MessageBus_Destroy, MESSAGE_BUS_HANDLE, bus) + ((RefCountObject*)bus)->dec_ref(); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, MESSAGE_BUS_RESULT, MessageBus_Publish, MESSAGE_BUS_HANDLE, bus, MESSAGE_HANDLE, message) + MESSAGE_BUS_RESULT busResult = currentMessageBusResult; + MOCK_METHOD_END(MESSAGE_BUS_RESULT, busResult) + + // ConstMap mocks + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, ConstMap_Create, MAP_HANDLE, sourceMap) + CONSTMAP_HANDLE result2; + currentConstMap_Create_call ++; + if (whenShallConstMap_Create_fail == currentConstMap_Create_call) + { + result2 = NULL; + } + else + { + result2 = (CONSTMAP_HANDLE)(new RefCountObject()); + } + MOCK_METHOD_END(CONSTMAP_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, ConstMap_Clone, CONSTMAP_HANDLE, handle) + CONSTMAP_HANDLE result3; + currentConstMap_Clone_call++; + if (whenShallConstMap_Clone_fail == currentConstMap_Clone_call) + { + result3 = NULL; + } + else + { + result3 = handle; + ((RefCountObject*)handle)->inc_ref(); + } + MOCK_METHOD_END(CONSTMAP_HANDLE, result3) + + MOCK_STATIC_METHOD_1(, MAP_HANDLE, ConstMap_CloneWriteable, CONSTMAP_HANDLE, handle) + MAP_HANDLE result4; + currentConstMap_CloneWriteable_call++; + if (currentConstMap_CloneWriteable_call == whenShallConstMap_CloneWriteable_fail) + result4 = NULL; + else + result4 = (MAP_HANDLE)(new RefCountObject()); + MOCK_METHOD_END(MAP_HANDLE, result4) + + MOCK_STATIC_METHOD_1(, void, ConstMap_Destroy, CONSTMAP_HANDLE, map) + ((RefCountObject*)map)->dec_ref(); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, const char*, ConstMap_GetValue, CONSTMAP_HANDLE, handle, const char*, key) + const char * result5 = VALID_VALUE; + if (strcmp(GW_MAC_ADDRESS_PROPERTY, key) == 0) + { + result5 = macAddressProperties; + } + else if (strcmp(GW_SOURCE_PROPERTY, key) == 0) + { + result5 = sourceProperties; + } + else if (strcmp(GW_DEVICENAME_PROPERTY, key) == 0) + { + result5 = deviceNameProperties; + } + else if (strcmp(GW_DEVICEKEY_PROPERTY, key) == 0) + { + result5 = deviceKeyProperties; + } + MOCK_METHOD_END(const char *, result5) + + // CONSTBUFFER mocks. + MOCK_STATIC_METHOD_2(, CONSTBUFFER_HANDLE, CONSTBUFFER_Create, const unsigned char*, source, size_t, size) + CONSTBUFFER_HANDLE result1; + currentCONSTBUFFER_Create_call++; + if (whenShallCONSTBUFFER_Create_fail == currentCONSTBUFFER_Create_call) + { + result1 = NULL; + } + else + { + result1 = (CONSTBUFFER_HANDLE)(new RefCountObject()); + } + MOCK_METHOD_END(CONSTBUFFER_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, CONSTBUFFER_HANDLE, CONSTBUFFER_Clone, CONSTBUFFER_HANDLE, constbufferHandle) + CONSTBUFFER_HANDLE result2; + currentCONSTBUFFER_Clone_call++; + if (currentCONSTBUFFER_Clone_call == whenShallCONSTBUFFER_Clone_fail) + { + result2 = NULL; + } + else + { + result2 = constbufferHandle; + ((RefCountObject*)constbufferHandle)->inc_ref(); + } + MOCK_METHOD_END(CONSTBUFFER_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, CONSTBUFFER_Destroy, CONSTBUFFER_HANDLE, constbufferHandle) + ((RefCountObject*)constbufferHandle)->dec_ref(); + MOCK_VOID_METHOD_END() + + + // Map related + + // Map_Clone + MOCK_STATIC_METHOD_1(, MAP_HANDLE, Map_Clone, MAP_HANDLE, sourceMap) + ((RefCountObject*)sourceMap)->inc_ref(); + MOCK_METHOD_END(MAP_HANDLE, sourceMap) + + // Map_Destroy + MOCK_STATIC_METHOD_1(, void, Map_Destroy, MAP_HANDLE, ptr) + ((RefCountObject*)ptr)->dec_ref(); + MOCK_VOID_METHOD_END() + + // Map_AddOrUpdate + MOCK_STATIC_METHOD_3(, MAP_RESULT, Map_AddOrUpdate, MAP_HANDLE, handle, const char*, key, const char*, value) + MAP_RESULT result7; + currentMap_call++; + if (currentMap_call == whenShallMap_fail) + { + result7 = MAP_ERROR; + } + else + { + result7 = MAP_OK; + } + MOCK_METHOD_END(MAP_RESULT, result7) + + // Message + MOCK_STATIC_METHOD_1(, MESSAGE_HANDLE, Message_Create, const MESSAGE_CONFIG*, cfg) + MESSAGE_HANDLE result2 = (MESSAGE_HANDLE)(new RefCountObject()); + MOCK_METHOD_END(MESSAGE_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, MESSAGE_HANDLE, Message_CreateFromBuffer, const MESSAGE_BUFFER_CONFIG*, cfg) + MESSAGE_HANDLE result1; + currentMessage_call++; + if (currentMessage_call == whenShallMessage_fail) + { + result1 = NULL; + } + else + { + result1 = (MESSAGE_HANDLE)(new RefCountObject()); + } + MOCK_METHOD_END(MESSAGE_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, MESSAGE_HANDLE, Message_Clone, MESSAGE_HANDLE, message) + ((RefCountObject*)message)->inc_ref(); + MOCK_METHOD_END(MESSAGE_HANDLE, message) + + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message) + CONSTMAP_HANDLE result1; + currentMessage_call++; + if (currentMessage_call == whenShallMessage_fail) + { + result1 = NULL; + } + else + { + result1 = ConstMap_Create((MAP_HANDLE)VALID_MAP_HANDLE); + } + MOCK_METHOD_END(CONSTMAP_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, const CONSTBUFFER*, Message_GetContent, MESSAGE_HANDLE, message) + CONSTBUFFER* result1 = &messageContent; + MOCK_METHOD_END(const CONSTBUFFER*, result1) + + MOCK_STATIC_METHOD_1(, CONSTBUFFER_HANDLE, Message_GetContentHandle, MESSAGE_HANDLE, message) + CONSTBUFFER_HANDLE result1; + currentMessage_call++; + if (currentMessage_call == whenShallMessage_fail) + { + result1 = NULL; + } + else + { + result1 = CONSTBUFFER_Create(NULL, 0); + } + MOCK_METHOD_END(CONSTBUFFER_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, void, Message_Destroy, MESSAGE_HANDLE, message) + ((RefCountObject*)message)->dec_ref(); + MOCK_VOID_METHOD_END() + + // vector.h + MOCK_STATIC_METHOD_1(, VECTOR_HANDLE, VECTOR_create, size_t, elementSize) + auto r = BASEIMPLEMENTATION::VECTOR_create(elementSize); + MOCK_METHOD_END(VECTOR_HANDLE, r) + + MOCK_STATIC_METHOD_1(, void, VECTOR_destroy, VECTOR_HANDLE, handle) + BASEIMPLEMENTATION::VECTOR_destroy(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements) + auto r = BASEIMPLEMENTATION::VECTOR_push_back( handle, elements, numElements); + MOCK_METHOD_END(int, r) + + MOCK_STATIC_METHOD_3(, void, VECTOR_erase, VECTOR_HANDLE, handle, void*, elements, size_t, numElements) + BASEIMPLEMENTATION::VECTOR_erase(handle, elements, numElements); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, void, VECTOR_clear, VECTOR_HANDLE, handle) + BASEIMPLEMENTATION::VECTOR_clear(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, void*, VECTOR_element, const VECTOR_HANDLE, handle, size_t, index) + currentVectorElement_call++; + void * result1; + if (currentVectorElement_call = whenShallVectorElement_fail) + { + result1 = NULL; + } + else + { + result1 = BASEIMPLEMENTATION::VECTOR_element(handle, index); + } + MOCK_METHOD_END(void*, result1) + + MOCK_STATIC_METHOD_1(, void*, VECTOR_front, const VECTOR_HANDLE, handle) + auto r = BASEIMPLEMENTATION::VECTOR_front( handle); + MOCK_METHOD_END(void*, r) + + MOCK_STATIC_METHOD_1(, void*, VECTOR_back, const VECTOR_HANDLE, handle) + auto r = BASEIMPLEMENTATION::VECTOR_back( handle); + MOCK_METHOD_END(void*, r) + + MOCK_STATIC_METHOD_3(, void*, VECTOR_find_if, const VECTOR_HANDLE, handle, PREDICATE_FUNCTION, pred, const void*, value) + auto r = BASEIMPLEMENTATION::VECTOR_find_if( handle, pred, value); + MOCK_METHOD_END(void*, r) + + MOCK_STATIC_METHOD_1(, size_t, VECTOR_size, const VECTOR_HANDLE, handle) + auto r = BASEIMPLEMENTATION::VECTOR_size( handle); + MOCK_METHOD_END(size_t, r) + + // crt_abstractions.h + + MOCK_STATIC_METHOD_2(, int, mallocAndStrcpy_s, char**, destination, const char*, source) + currentStrdup_call++; + int r; + if (currentStrdup_call == whenShallStrdup_fail) + { + r = 1; + } + else + { + if (source == NULL) + { + *destination = NULL; + r = 1; + } + else + { + *destination = (char*)BASEIMPLEMENTATION::gballoc_malloc(strlen(source) + 1); + strcpy(*destination, source); + r = 0; + } + } + MOCK_METHOD_END(int,r) + + + MOCK_STATIC_METHOD_3(, int, unsignedIntToString, char*, destination, size_t, destinationSize, unsigned int, value) + MOCK_METHOD_END(int, 0) + + MOCK_STATIC_METHOD_3(, int, size_tToString, char*, destination, size_t, destinationSize, size_t, value) + MOCK_METHOD_END(int, 0) + + }; + +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_2(CIdentitymapMocks, , void*, gballoc_realloc, void*, ptr, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , void, gballoc_free, void*, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_0(CIdentitymapMocks, , MESSAGE_BUS_HANDLE, MessageBus_Create); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , void, MessageBus_Destroy, MESSAGE_BUS_HANDLE, bus); +DECLARE_GLOBAL_MOCK_METHOD_2(CIdentitymapMocks, , MESSAGE_BUS_RESULT, MessageBus_Publish, MESSAGE_BUS_HANDLE, bus, MESSAGE_HANDLE, message); + +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , CONSTMAP_HANDLE, ConstMap_Create, MAP_HANDLE, sourceMap); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , CONSTMAP_HANDLE, ConstMap_Clone, CONSTMAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , void, ConstMap_Destroy, CONSTMAP_HANDLE, map); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , MAP_HANDLE, ConstMap_CloneWriteable, CONSTMAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_2(CIdentitymapMocks, , const char*, ConstMap_GetValue, CONSTMAP_HANDLE, handle, const char *, key); + +DECLARE_GLOBAL_MOCK_METHOD_2(CIdentitymapMocks, , CONSTBUFFER_HANDLE, CONSTBUFFER_Create, const unsigned char*, source, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , CONSTBUFFER_HANDLE, CONSTBUFFER_Clone, CONSTBUFFER_HANDLE, constbufferHandle); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , void, CONSTBUFFER_Destroy, CONSTBUFFER_HANDLE, constbufferHandle); + +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , MAP_HANDLE, Map_Clone, MAP_HANDLE, sourceMap); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , void, Map_Destroy, MAP_HANDLE, ptr); +DECLARE_GLOBAL_MOCK_METHOD_3(CIdentitymapMocks, , MAP_RESULT, Map_AddOrUpdate, MAP_HANDLE, handle, const char*, key, const char*, value); + +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , MESSAGE_HANDLE, Message_Create, const MESSAGE_CONFIG*, cfg); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , MESSAGE_HANDLE, Message_CreateFromBuffer, const MESSAGE_BUFFER_CONFIG*, cfg); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , MESSAGE_HANDLE, Message_Clone, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , const CONSTBUFFER*, Message_GetContent, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , CONSTBUFFER_HANDLE, Message_GetContentHandle, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , void, Message_Destroy, MESSAGE_HANDLE, message); + +// vector.h +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , VECTOR_HANDLE, VECTOR_create, size_t, elementSize); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , void, VECTOR_destroy, VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CIdentitymapMocks, , int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements); +DECLARE_GLOBAL_MOCK_METHOD_3(CIdentitymapMocks, , void, VECTOR_erase, VECTOR_HANDLE, handle, void*, elements, size_t, numElements); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , void, VECTOR_clear, VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_2(CIdentitymapMocks, , void*, VECTOR_element, const VECTOR_HANDLE, handle, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , void*, VECTOR_front, const VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , void*, VECTOR_back, const VECTOR_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_3(CIdentitymapMocks, , void*, VECTOR_find_if, const VECTOR_HANDLE, handle, PREDICATE_FUNCTION, pred, const void*, value); +DECLARE_GLOBAL_MOCK_METHOD_1(CIdentitymapMocks, , size_t, VECTOR_size, const VECTOR_HANDLE, handle); + +DECLARE_GLOBAL_MOCK_METHOD_2(CIdentitymapMocks, , int, mallocAndStrcpy_s, char**, destination, const char*, source); +DECLARE_GLOBAL_MOCK_METHOD_3(CIdentitymapMocks, , int, unsignedIntToString, char*, destination, size_t, destinationSize, unsigned int, value); +DECLARE_GLOBAL_MOCK_METHOD_3(CIdentitymapMocks, , int, size_tToString, char*, destination, size_t, destinationSize, size_t, value); + +BEGIN_TEST_SUITE(identitymap_unittests) + + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + + CIdentitymapMocks mocks; + + currentmalloc_call = 0; + whenShallmalloc_fail = 0; + + currentConstMap_Create_call = 0; + whenShallConstMap_Create_fail = 0; + currentConstMap_Clone_call = 0; + whenShallConstMap_Clone_fail = 0; + currentCONSTBUFFER_Create_call = 0; + whenShallCONSTBUFFER_Create_fail = 0; + currentCONSTBUFFER_Clone_call = 0; + whenShallCONSTBUFFER_Clone_fail = 0; + currentStrdup_call = 0; + whenShallStrdup_fail = 0; + currentVectorElement_call = 0; + whenShallVectorElement_fail = 0; + macAddressProperties = NULL; + sourceProperties = NULL; + deviceNameProperties = NULL; + deviceKeyProperties = NULL; + currentMessage_call = 0; + whenShallMessage_fail = 0; + currentConstMap_CloneWriteable_call = 0; + whenShallConstMap_CloneWriteable_fail = 0; + currentMap_call = 0; + whenShallMap_fail = 0; + currentMessageBusResult = MESSAGE_BUS_OK; + + testVector1 = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + IDENTITY_MAP_CONFIG c1 = + { + "aa:Aa:bb:bB:cc:CC", + "aNiceDevice", + "aNiceKey" + }; + VECTOR_push_back(testVector1, &c1, 1); + + testVector2 = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + IDENTITY_MAP_CONFIG c2 = + { + "aa:Aa:bb:bB:cc:BB", + "a2ndDevice", + "a2ndKey" + }; + VECTOR_push_back(testVector2, &c1, 1); + VECTOR_push_back(testVector2, &c2, 1); + mocks.ResetAllCalls(); + + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + CIdentitymapMocks mocks; + VECTOR_destroy(testVector1); + VECTOR_destroy(testVector2); + mocks.ResetAllCalls(); + + } + + /*Tests_SRS_IDMAP_17_001 [ Module_GetAPIs shall return a non-NULL pointer to a MODULE_APIS structure.] */ + /*Tests_SRS_IDMAP_17_002: [The MODULE_APIS structure shall have non-NULL Module_Create, Module_Destroy, and Module_Receive fields.]*/ + TEST_FUNCTION(IdentityMap_Module_GetAPIs_Success) + { + ///Arrange + CIdentitymapMocks mocks; + + ///Act + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + ///Assert + ASSERT_IS_NOT_NULL(theAPIS); + ASSERT_IS_NOT_NULL((void*)theAPIS->Module_Create); + ASSERT_IS_NOT_NULL((void*)theAPIS->Module_Destroy); + ASSERT_IS_NOT_NULL((void*)theAPIS->Module_Receive); + + ///Ablution + } + + /*Tests_SRS_IDMAP_17_004: [If the busHandle is NULL, this function shall fail and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_MessageBus_Null) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + MESSAGE_BUS_HANDLE bus = NULL; + unsigned char config; + + ///Act + auto n = theAPIS->Module_Create(bus, &config); + + ///Assert + ASSERT_IS_NULL(n); + + ///Ablution + } + + /*Tests_SRS_IDMAP_17_005: [If the configuration is NULL, this function shall fail and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_Config_Null) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + void * config = NULL; + + ///Act + auto n = theAPIS->Module_Create(bus, config); + + ///Assert + ASSERT_IS_NULL(n); + + ///Ablution + } + + /*Tests_SRS_IDMAP_17_003: [Upon success, this function shall return a valid pointer to a MODULE_HANDLE.]*/ + TEST_FUNCTION(IdentityMap_Create_Success_SingleEntry) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module struct*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the d2c internal array*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the c2d internal array*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + + ///Act + auto n = theAPIS->Module_Create(bus, testVector1); + + ///Assert + ASSERT_IS_NOT_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + theAPIS->Module_Destroy(n); + } + + /*Tests_SRS_IDMAP_17_041: [If the configuration has no vector elements, this function shall fail and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_ValidateConfig_Empty_Vector) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + + + ///Act + auto n = theAPIS->Module_Create(bus, v); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + VECTOR_destroy(v); + } + + /*Tests_SRS_IDMAP_17_019: [If any macAddress, deviceId or deviceKey are NULL, this function shall fail and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_ValidateConfig_NULL_fields) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v1 = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + IDENTITY_MAP_CONFIG c1 = + { + "aa:Aa:bb:bB:cc:CC", + NULL, + "aNiceKey" + }; + VECTOR_push_back(v1, &c1, 1); + VECTOR_HANDLE v2 = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + IDENTITY_MAP_CONFIG c2 = + { + "aa:Aa:bb:bB:cc:CC", + "aNiceDevice", + NULL + }; + VECTOR_push_back(v2, &c2, 1); + VECTOR_HANDLE v3 = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + IDENTITY_MAP_CONFIG c3 = + { + NULL, + "aNiceDevice", + "aNiceKey" + }; + VECTOR_push_back(v3, &c3, 1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + + ///Act + auto n1 = theAPIS->Module_Create(bus, v1); + ASSERT_IS_NULL(n1); + auto n2 = theAPIS->Module_Create(bus, v2); + ASSERT_IS_NULL(n2); + auto n3 = theAPIS->Module_Create(bus, v3); + ASSERT_IS_NULL(n3); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + VECTOR_destroy(v1); + VECTOR_destroy(v2); + VECTOR_destroy(v3); + } + + /*Tests_SRS_IDMAP_17_006: [If any macAddress string in configuration is not a MAC address in canonical form, this function shall fail and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_ValidateConfig_non_Canon_Mac1) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v1 = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + // wrong length + IDENTITY_MAP_CONFIG c1 = + { + "aa-Aa-bb-B-cc-CC", + "aNiceDevice", + "aNiceKey" + }; + VECTOR_push_back(v1, &c1, 1); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + + + ///Act + auto n1 = theAPIS->Module_Create(bus, v1); + ASSERT_IS_NULL(n1); + + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + VECTOR_destroy(v1); + + } + + /*Tests_SRS_IDMAP_17_006: [If any macAddress string in configuration is not a MAC address in canonical form, this function shall fail and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_ValidateConfig_non_Canon_Mac2) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + VECTOR_HANDLE v2 = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + // incorrect hexadecimal + IDENTITY_MAP_CONFIG c2 = + { + "aa:Aa:bb:bB:cg:CC", + "aNiceDevice", + "aNiceKey" + }; + VECTOR_push_back(v2, &c2, 1); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + + + ///Act + + auto n2 = theAPIS->Module_Create(bus, v2); + ASSERT_IS_NULL(n2); + + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + + VECTOR_destroy(v2); + } + + /*Tests_SRS_IDMAP_17_006: [If any macAddress string in configuration is not a MAC address in canonical form, this function shall fail and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_ValidateConfig_non_Canon_Mac3) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + VECTOR_HANDLE v3 = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + // Valid character instead of dash + IDENTITY_MAP_CONFIG c3 = + { + "aa-Aa-bb-bB-cccCC", + "aNiceDevice", + "aNiceKey" + }; + VECTOR_push_back(v3, &c3, 1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + + + ///Act + + auto n3 = theAPIS->Module_Create(bus, v3); + ASSERT_IS_NULL(n3); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + + VECTOR_destroy(v3); + } + + /*Tests_SRS_IDMAP_17_010: [If IdentityMap_Create fails to allocate a new IDENTITY_MAP_DATA structure, then this function shall fail, and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_Module_alloc_fails) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + whenShallmalloc_fail = 1; + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module struct*/ + .IgnoreArgument(1); + + + ///Act + auto n = theAPIS->Module_Create(bus, testVector1); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + } + + /*Tests_SRS_IDMAP_17_011: [If IdentityMap_Create fails to create memory for the macToDeviceArray, then this function shall fail and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_internal_d2c_alloc_fail) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + whenShallmalloc_fail = 2; + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module struct*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the internal array*/ + .IgnoreArgument(1); + + + ///Act + auto n = theAPIS->Module_Create(bus, testVector1); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + } + + /*Tests_SRS_IDMAP_17_042: [ If IdentityMap_Create fails to create memory for the deviceToMacArray, then this function shall fail and return NULL. */ + TEST_FUNCTION(IdentityMap_Create_internal_c2d_alloc_fail) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module struct*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the internal array*/ + .IgnoreArgument(1) + .SetFailReturn((void_ptr)NULL); + + + ///Act + auto n = theAPIS->Module_Create(bus, testVector1); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + } + /*Tests_SRS_IDMAP_17_012: [If IdentityMap_Create fails to add a MAC address triplet to the macToDeviceArray, then this function shall fail, release all resources, and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_DeepCopy_fail_mac1) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + whenShallStrdup_fail =7; + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module struct*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the D2C internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the C2D internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + /* 1st vector element */ + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + /* 2nd vector element */ + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + + ///Act + auto n = theAPIS->Module_Create(bus, testVector2); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + } + + //Tests_SRS_IDMAP_17_043: [ If IdentityMap_Create fails to add a MAC address triplet to the deviceToMacArray, then this function shall fail, release all resources, and return NULL. + TEST_FUNCTION(IdentityMap_Create_DeepCopy_fail_mac2) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + whenShallStrdup_fail = 10; + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module struct*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the D2C internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the C2D internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + /* 1st vector element */ + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + /* 2nd vector element */ + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + + ///Act + auto n = theAPIS->Module_Create(bus, testVector2); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + } + + /*Tests_SRS_IDMAP_17_012: [If IdentityMap_Create fails to add a MAC address triplet to the macToDeviceArray, then this function shall fail, release all resources, and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_DeepCopy_fail_id1) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + + whenShallStrdup_fail = 8; + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module struct*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the d2c internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the c2d internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + //1st vector element + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + /* 2nd vector element */ + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + + ///Act + auto n = theAPIS->Module_Create(bus, testVector2); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + } + + TEST_FUNCTION(IdentityMap_Create_DeepCopy_fail_id2) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + + whenShallStrdup_fail = 11; + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module struct*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the d2c internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the c2d internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + //1st vector element + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + /* 2nd vector element */ + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + + ///Act + auto n = theAPIS->Module_Create(bus, testVector2); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + } + + + /*Tests_SRS_IDMAP_17_012: [If IdentityMap_Create fails to add a MAC address triplet to the macToDeviceArray, then this function shall fail, release all resources, and return NULL.]*/ + TEST_FUNCTION(IdentityMap_Create_DeepCopy_fail_key1) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + whenShallStrdup_fail = 9; + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module struct*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the D2C internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the C2D internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + //1st vector element + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + // 2nd vector element + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + + + + ///Act + auto n = theAPIS->Module_Create(bus, testVector2); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + } + + TEST_FUNCTION(IdentityMap_Create_DeepCopy_fail_key2) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + + whenShallStrdup_fail = 12; + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the module struct*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the D2C internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is for the C2D internal array*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)).IgnoreArgument(1); + + //1st vector element + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + // 2nd vector element + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the mac address*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device name*/ + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is for the device key*/ + .IgnoreAllArguments(); + + + + ///Act + auto n = theAPIS->Module_Create(bus, testVector2); + + ///Assert + ASSERT_IS_NULL(n); + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + } + + /*Tests_SRS_IDMAP_17_018: [If moduleHandle is NULL, IdentityMap_Destroy shall return.]*/ + TEST_FUNCTION(IdentityMap_Destroy_NULL) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + ///Act + theAPIS->Module_Destroy(NULL); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + } + + /*Tests_SRS_IDMAP_17_015: [IdentityMap_Destroy shall release all resources allocated for the module.]*/ + TEST_FUNCTION(IdentityMap_Destroy_2Element) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + + auto n = theAPIS->Module_Create(bus, testVector2); + + mocks.ResetAllCalls(); + + //1st vector element + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + //2nd vector element + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + //internal struct and module data + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + + ///Act + theAPIS->Module_Destroy(n); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_020: [If moduleHandle or messageHandle is NULL, then the function shall return.]*/ + TEST_FUNCTION(IdentityMap_Receive_Null_inputs) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + + ///Act + theAPIS->Module_Receive((MODULE_HANDLE)&fake, NULL); + theAPIS->Module_Receive(NULL, (MESSAGE_HANDLE)&fake); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + } + + TEST_FUNCTION(IdentityMap_Receive_no_source) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + /*Tests_SRS_IDMAP_17_021: [If messageHandle properties does not contain "macAddress" property, then the function shall return.]*/ + TEST_FUNCTION(IdentityMap_Receive_D2C_no_Mac) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_024: [If messageHandle properties contains properties "deviceName" and "deviceKey", then this function shall return.]*/ + TEST_FUNCTION(IdentityMap_Receive_has_D2C_device_name_and_key) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "aa:Aa:bb:bB:cc:BB"; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + deviceKeyProperties = "APossiblyValidKey"; + deviceNameProperties = "APossiblyValidName"; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICEKEY_PROPERTY)) + .IgnoreArgument(1); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_024: [If messageHandle properties contains properties "deviceName" and "deviceKey", then this function shall return.]*/ + TEST_FUNCTION(IdentityMap_when_received_D2C_message_has_has_device_name_key_and_any_source_then_no_republish_happens) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "aa:Aa:bb:bB:cc:BB"; + sourceProperties = "anySource"; + deviceKeyProperties = "APossiblyValidKey"; + deviceNameProperties = "APossiblyValidName"; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICEKEY_PROPERTY)) + .IgnoreArgument(1); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + + /*Tests_SRS_IDMAP_17_040: [If the macAddress of the message is not in canonical form, then this function shall return.]*/ + TEST_FUNCTION(IdentityMap_Receive_D2C_not_canon_mac) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = GW_SOURCE_BLE_TELEMETRY; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + deviceNameProperties = "SomeDeviceName"; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICEKEY_PROPERTY)) + .IgnoreArgument(1); + + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_025: [If the macAddress of the message is not found in the macToDeviceArray list, then this function shall return.] */ + TEST_FUNCTION(IdentityMap_Receive_D2C_mac_not_found) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector1); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "00:00:00:00:00:00"; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + deviceNameProperties = "SomeDeviceName"; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICEKEY_PROPERTY)) + .IgnoreArgument(1); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_021: [If messageHandle properties does not contain "macAddress" property, then the function shall return.]*/ + TEST_FUNCTION(IdentityMap_Receive_D2C_get_properties_2_fail) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "aa:aa:bb:bb:cc:cc"; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + + whenShallMessage_fail = 2; + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_027: [If ConstMap_CloneWriteable fails, IdentityMap_Receive shall deallocate any resources and return.]*/ + TEST_FUNCTION(IdentityMap_Receive_D2C_CloneWriteable_fails) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "aa:aa:bb:bb:cc:cc"; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + whenShallConstMap_CloneWriteable_fail = 1; + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_029: [If adding deviceName fails,IdentityMap_Receive shall deallocate all resources and return.]*/ + TEST_FUNCTION(IdentityMap_Receive_D2C_MapAdd1_fail) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "aa:aa:bb:bb:cc:cc"; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + whenShallMap_fail = 1; + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY, "aNiceDevice")).IgnoreArgument(1); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_031: [If adding deviceKey fails, IdentityMap_Receive shall deallocate all resources and return.]*/ + TEST_FUNCTION(IdentityMap_Receive_D2C_MapAdd2_fail) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "aa:aa:bb:bb:cc:cc"; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + whenShallMap_fail = 2; + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY, "aNiceDevice")).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICEKEY_PROPERTY, "aNiceKey")).IgnoreArgument(1); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_033: [If adding source fails, IdentityMap_Receive shall deallocate all resources and return.]*/ + TEST_FUNCTION(IdentityMap_Receive_D2C_MapAdd3_fail) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "aa:aa:bb:bb:cc:cc"; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + whenShallMap_fail = 3; + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY, "aNiceDevice")).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICEKEY_PROPERTY, "aNiceKey")).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_IDMAP_MODULE)).IgnoreArgument(1); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_035: [If cloning message content fails, IdentityMap_Receive shall deallocate all resources and return.]*/ + TEST_FUNCTION(IdentityMap_Receive_D2C_Message_GetContent_fail) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "aa:aa:bb:bb:cc:cc"; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY, "aNiceDevice")).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICEKEY_PROPERTY, "aNiceKey")).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_IDMAP_MODULE)).IgnoreArgument(1); + whenShallMessage_fail = 3; + STRICT_EXPECTED_CALL(mocks, Message_GetContentHandle(m)); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_037: [If creating new message fails, IdentityMap_Receive shall deallocate all resources and return.]*/ + TEST_FUNCTION(IdentityMap_Receive_D2C_Message_CreateFromBuffer_fail) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "aa:aa:bb:bb:cc:cc"; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY, "aNiceDevice")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICEKEY_PROPERTY, "aNiceKey")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_IDMAP_MODULE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetContentHandle(m)); + whenShallMessage_fail = 4; + STRICT_EXPECTED_CALL(mocks, Message_CreateFromBuffer(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_038: [IdentityMap_Receive shall call MessageBus_Publish with busHandle and new message.]*/ + TEST_FUNCTION(IdentityMap_Receive_D2C_MessageBus_Publish_fail) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = MessageBus_Create(); + auto n = theAPIS->Module_Create(bus, testVector2); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "aa:aa:bb:bb:cc:cc"; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + + mocks.ResetAllCalls(); + + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY, "aNiceDevice")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICEKEY_PROPERTY, "aNiceKey")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_IDMAP_MODULE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetContentHandle(m)); + STRICT_EXPECTED_CALL(mocks, Message_CreateFromBuffer(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + currentMessageBusResult = MESSAGE_BUS_ERROR; + STRICT_EXPECTED_CALL(mocks, MessageBus_Publish(bus, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + theAPIS->Module_Destroy(n); + MessageBus_Destroy(bus); + + } + + /*Tests_SRS_IDMAP_17_026: [On a message which passes all checks, IdentityMap_Receive shall call ConstMap_CloneWriteable on the message properties.]*/ + /*Tests_SRS_IDMAP_17_028: [IdentityMap_Receive shall call Map_AddOrUpdate with key of "deviceName" and value of found deviceId.]*/ + /*Tests_SRS_IDMAP_17_032: [IdentityMap_Receive shall call Map_AddOrUpdate with key of "source" and value of "mapping".]*/ + /*Tests_SRS_IDMAP_17_030: [IdentityMap_Receive shall call Map_AddOrUpdate with key of "deviceKey" and value of found deviceKey.]*/ + /*Tests_SRS_IDMAP_17_034: [IdentityMap_Receive shall clone message content.]*/ + /*Tests_SRS_IDMAP_17_036: [IdentityMap_Receive shall create a new message by calling Message_Create with new map and cloned content.]*/ + /*Tests_SRS_IDMAP_17_038: [IdentityMap_Receive shall call MessageBus_Publish with busHandle and new message.]*/ + /*Tests_SRS_IDMAP_17_039: [IdentityMap_Receive will destroy all resources it created.]*/ + TEST_FUNCTION(IdentityMap_Receive_D2C_Success) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + + IDENTITY_MAP_CONFIG c1 = { "01:01:01:01:01:01", "Sensor1", "theKeyFor1" }; + IDENTITY_MAP_CONFIG c2 = { "02:02:02:02:02:02", "Sensor2", "theKeyFor2" }; + IDENTITY_MAP_CONFIG c3 = { "03:03:03:03:03:03", "Sensor3", "theKeyFor3" }; + IDENTITY_MAP_CONFIG c4 = { "04:04:04:04:04:04", "Sensor4", "theKeyFor4" }; + IDENTITY_MAP_CONFIG c5 = { "05:05:05:05:05:05", "Sensor5", "theKeyFor5" }; + IDENTITY_MAP_CONFIG c6 = { "06:06:06:06:06:06", "Sensor6", "theKeyFor6" }; + IDENTITY_MAP_CONFIG c7 = { "07:07:07:07:07:07", "Sensor7", "theKeyFor7" }; + IDENTITY_MAP_CONFIG c8 = { "08:08:08:08:08:08", "Sensor8", "theKeyFor8" }; + IDENTITY_MAP_CONFIG c9 = { "09:09:09:09:09:09", "Sensor9", "theKeyFor9" }; + VECTOR_push_back(v, &c1, 1); + VECTOR_push_back(v, &c2, 1); + VECTOR_push_back(v, &c3, 1); + VECTOR_push_back(v, &c4, 1); + VECTOR_push_back(v, &c5, 1); + VECTOR_push_back(v, &c6, 1); + VECTOR_push_back(v, &c7, 1); + VECTOR_push_back(v, &c8, 1); + VECTOR_push_back(v, &c9, 1); + auto n = theAPIS->Module_Create(bus, v); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "07:07:07:07:07:07"; + sourceProperties = GW_SOURCE_BLE_TELEMETRY; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY, "Sensor7")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_DEVICEKEY_PROPERTY, "theKeyFor7")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_IDMAP_MODULE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetContentHandle(m)); + STRICT_EXPECTED_CALL(mocks, Message_CreateFromBuffer(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Publish((MESSAGE_BUS_HANDLE)&fake, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + VECTOR_destroy(v); + theAPIS->Module_Destroy(n); + + } + + //Tests_SRS_IDMAP_17_049: [ On a C2D message received, IdentityMap_Receive shall call ConstMap_CloneWriteable on the message properties. ] + //Tests_SRS_IDMAP_17_051: [ IdentityMap_Receive shall call Map_AddOrUpdate with key of "macAddress" and value of found macAddress. ] + //Tests_SRS_IDMAP_17_032: [IdentityMap_Receive shall call Map_AddOrUpdate with key of "source" and value of "mapping".] + //Tests_SRS_IDMAP_17_034: [IdentityMap_Receive shall clone message content.] + //Tests_SRS_IDMAP_17_036: [IdentityMap_Receive shall create a new message by calling Message_CreateFromBuffer with new map and cloned content.] + //Tests_SRS_IDMAP_17_038: [IdentityMap_Receive shall call MessageBus_Publish with busHandle and new message.] + TEST_FUNCTION(IdentityMap_Receive_C2D_Success) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + + IDENTITY_MAP_CONFIG c1 = { "01:01:01:01:01:01", "Sensor1", "theKeyFor1" }; + IDENTITY_MAP_CONFIG c2 = { "02:02:02:02:02:02", "Sensor2", "theKeyFor2" }; + IDENTITY_MAP_CONFIG c3 = { "03:03:03:03:03:03", "Sensor3", "theKeyFor3" }; + IDENTITY_MAP_CONFIG c4 = { "04:04:04:04:04:04", "Sensor4", "theKeyFor4" }; + IDENTITY_MAP_CONFIG c5 = { "05:05:05:05:05:05", "Sensor5", "theKeyFor5" }; + IDENTITY_MAP_CONFIG c6 = { "06:06:06:06:06:06", "Sensor6", "theKeyFor6" }; + IDENTITY_MAP_CONFIG c7 = { "07:07:07:07:07:07", "Sensor7", "theKeyFor7" }; + IDENTITY_MAP_CONFIG c8 = { "08:08:08:08:08:08", "Sensor8", "theKeyFor8" }; + IDENTITY_MAP_CONFIG c9 = { "09:09:09:09:09:09", "Sensor9", "theKeyFor9" }; + VECTOR_push_back(v, &c1, 1); + VECTOR_push_back(v, &c2, 1); + VECTOR_push_back(v, &c3, 1); + VECTOR_push_back(v, &c4, 1); + VECTOR_push_back(v, &c5, 1); + VECTOR_push_back(v, &c6, 1); + VECTOR_push_back(v, &c7, 1); + VECTOR_push_back(v, &c8, 1); + VECTOR_push_back(v, &c9, 1); + auto n = theAPIS->Module_Create(bus, v); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + deviceNameProperties = "Sensor7"; + sourceProperties = GW_IOTHUB_MODULE; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY, "07:07:07:07:07:07")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_IDMAP_MODULE)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_GetContentHandle(m)); + STRICT_EXPECTED_CALL(mocks, Message_CreateFromBuffer(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Create(IGNORED_PTR_ARG, IGNORED_NUM_ARG)) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, CONSTBUFFER_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Publish((MESSAGE_BUS_HANDLE)&fake, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + VECTOR_destroy(v); + theAPIS->Module_Destroy(n); + + } + + //Tests_SRS_IDMAP_17_044: [ If messageHandle properties contains a "source" property that is set to "mapping", the message shall not be marked as a D2C message. ] + TEST_FUNCTION(IdentityMap_Receive_C2D_MapUpdate_source_fails) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + + IDENTITY_MAP_CONFIG c1 = { "01:01:01:01:01:01", "Sensor1", "theKeyFor1" }; + IDENTITY_MAP_CONFIG c2 = { "02:02:02:02:02:02", "Sensor2", "theKeyFor2" }; + IDENTITY_MAP_CONFIG c3 = { "03:03:03:03:03:03", "Sensor3", "theKeyFor3" }; + IDENTITY_MAP_CONFIG c4 = { "04:04:04:04:04:04", "Sensor4", "theKeyFor4" }; + IDENTITY_MAP_CONFIG c5 = { "05:05:05:05:05:05", "Sensor5", "theKeyFor5" }; + IDENTITY_MAP_CONFIG c6 = { "06:06:06:06:06:06", "Sensor6", "theKeyFor6" }; + IDENTITY_MAP_CONFIG c7 = { "07:07:07:07:07:07", "Sensor7", "theKeyFor7" }; + IDENTITY_MAP_CONFIG c8 = { "08:08:08:08:08:08", "Sensor8", "theKeyFor8" }; + IDENTITY_MAP_CONFIG c9 = { "09:09:09:09:09:09", "Sensor9", "theKeyFor9" }; + VECTOR_push_back(v, &c1, 1); + VECTOR_push_back(v, &c2, 1); + VECTOR_push_back(v, &c3, 1); + VECTOR_push_back(v, &c4, 1); + VECTOR_push_back(v, &c5, 1); + VECTOR_push_back(v, &c6, 1); + VECTOR_push_back(v, &c7, 1); + VECTOR_push_back(v, &c8, 1); + VECTOR_push_back(v, &c9, 1); + auto n = theAPIS->Module_Create(bus, v); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + deviceNameProperties = "Sensor7"; + sourceProperties = GW_IOTHUB_MODULE; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY, "07:07:07:07:07:07")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY, GW_IDMAP_MODULE)) + .IgnoreArgument(1) + .SetFailReturn(MAP_ERROR); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + VECTOR_destroy(v); + theAPIS->Module_Destroy(n); + + } + + //Tests_SRS_IDMAP_17_052: [ If adding macAddress fails, IdentityMap_Receive shall deallocate all resources and return. ] + + TEST_FUNCTION(IdentityMap_Receive_C2D_MapUpdate_mac_fails) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + + IDENTITY_MAP_CONFIG c1 = { "01:01:01:01:01:01", "Sensor1", "theKeyFor1" }; + IDENTITY_MAP_CONFIG c2 = { "02:02:02:02:02:02", "Sensor2", "theKeyFor2" }; + IDENTITY_MAP_CONFIG c3 = { "03:03:03:03:03:03", "Sensor3", "theKeyFor3" }; + IDENTITY_MAP_CONFIG c4 = { "04:04:04:04:04:04", "Sensor4", "theKeyFor4" }; + IDENTITY_MAP_CONFIG c5 = { "05:05:05:05:05:05", "Sensor5", "theKeyFor5" }; + IDENTITY_MAP_CONFIG c6 = { "06:06:06:06:06:06", "Sensor6", "theKeyFor6" }; + IDENTITY_MAP_CONFIG c7 = { "07:07:07:07:07:07", "Sensor7", "theKeyFor7" }; + IDENTITY_MAP_CONFIG c8 = { "08:08:08:08:08:08", "Sensor8", "theKeyFor8" }; + IDENTITY_MAP_CONFIG c9 = { "09:09:09:09:09:09", "Sensor9", "theKeyFor9" }; + VECTOR_push_back(v, &c1, 1); + VECTOR_push_back(v, &c2, 1); + VECTOR_push_back(v, &c3, 1); + VECTOR_push_back(v, &c4, 1); + VECTOR_push_back(v, &c5, 1); + VECTOR_push_back(v, &c6, 1); + VECTOR_push_back(v, &c7, 1); + VECTOR_push_back(v, &c8, 1); + VECTOR_push_back(v, &c9, 1); + auto n = theAPIS->Module_Create(bus, v); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + deviceNameProperties = "Sensor7"; + sourceProperties = GW_IOTHUB_MODULE; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, GW_MAC_ADDRESS_PROPERTY, "07:07:07:07:07:07")) + .IgnoreArgument(1) + .SetFailReturn(MAP_ERROR); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + VECTOR_destroy(v); + theAPIS->Module_Destroy(n); + + } + + //Tests_SRS_IDMAP_17_050: [ If ConstMap_CloneWriteable fails, IdentityMap_Receive shall deallocate any resources and return. ] + TEST_FUNCTION(IdentityMap_Receive_C2D_clone_writeable_fails) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + + IDENTITY_MAP_CONFIG c1 = { "01:01:01:01:01:01", "Sensor1", "theKeyFor1" }; + IDENTITY_MAP_CONFIG c2 = { "02:02:02:02:02:02", "Sensor2", "theKeyFor2" }; + IDENTITY_MAP_CONFIG c3 = { "03:03:03:03:03:03", "Sensor3", "theKeyFor3" }; + IDENTITY_MAP_CONFIG c4 = { "04:04:04:04:04:04", "Sensor4", "theKeyFor4" }; + IDENTITY_MAP_CONFIG c5 = { "05:05:05:05:05:05", "Sensor5", "theKeyFor5" }; + IDENTITY_MAP_CONFIG c6 = { "06:06:06:06:06:06", "Sensor6", "theKeyFor6" }; + IDENTITY_MAP_CONFIG c7 = { "07:07:07:07:07:07", "Sensor7", "theKeyFor7" }; + IDENTITY_MAP_CONFIG c8 = { "08:08:08:08:08:08", "Sensor8", "theKeyFor8" }; + IDENTITY_MAP_CONFIG c9 = { "09:09:09:09:09:09", "Sensor9", "theKeyFor9" }; + VECTOR_push_back(v, &c1, 1); + VECTOR_push_back(v, &c2, 1); + VECTOR_push_back(v, &c3, 1); + VECTOR_push_back(v, &c4, 1); + VECTOR_push_back(v, &c5, 1); + VECTOR_push_back(v, &c6, 1); + VECTOR_push_back(v, &c7, 1); + VECTOR_push_back(v, &c8, 1); + VECTOR_push_back(v, &c9, 1); + auto n = theAPIS->Module_Create(bus, v); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + deviceNameProperties = "Sensor7"; + sourceProperties = GW_IOTHUB_MODULE; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((MAP_HANDLE)NULL); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + VECTOR_destroy(v); + theAPIS->Module_Destroy(n); + + } + + //Tests_SRS_IDMAP_17_046: [ If messageHandle properties does not contain a "source" property, then the message shall not be marked as a C2D message. ] + TEST_FUNCTION(IdentityMap_Receive_C2D_getproperties_fails) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + + IDENTITY_MAP_CONFIG c1 = { "01:01:01:01:01:01", "Sensor1", "theKeyFor1" }; + IDENTITY_MAP_CONFIG c2 = { "02:02:02:02:02:02", "Sensor2", "theKeyFor2" }; + IDENTITY_MAP_CONFIG c3 = { "03:03:03:03:03:03", "Sensor3", "theKeyFor3" }; + IDENTITY_MAP_CONFIG c4 = { "04:04:04:04:04:04", "Sensor4", "theKeyFor4" }; + IDENTITY_MAP_CONFIG c5 = { "05:05:05:05:05:05", "Sensor5", "theKeyFor5" }; + IDENTITY_MAP_CONFIG c6 = { "06:06:06:06:06:06", "Sensor6", "theKeyFor6" }; + IDENTITY_MAP_CONFIG c7 = { "07:07:07:07:07:07", "Sensor7", "theKeyFor7" }; + IDENTITY_MAP_CONFIG c8 = { "08:08:08:08:08:08", "Sensor8", "theKeyFor8" }; + IDENTITY_MAP_CONFIG c9 = { "09:09:09:09:09:09", "Sensor9", "theKeyFor9" }; + VECTOR_push_back(v, &c1, 1); + VECTOR_push_back(v, &c2, 1); + VECTOR_push_back(v, &c3, 1); + VECTOR_push_back(v, &c4, 1); + VECTOR_push_back(v, &c5, 1); + VECTOR_push_back(v, &c6, 1); + VECTOR_push_back(v, &c7, 1); + VECTOR_push_back(v, &c8, 1); + VECTOR_push_back(v, &c9, 1); + auto n = theAPIS->Module_Create(bus, v); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + deviceNameProperties = "Sensor7"; + sourceProperties = GW_IOTHUB_MODULE; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)) + .SetFailReturn((CONSTMAP_HANDLE)NULL); + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + VECTOR_destroy(v); + theAPIS->Module_Destroy(n); + + } + + //Tests_SRS_IDMAP_17_048: [ If the deviceName of the message is not found in deviceToMacArray, then the message shall not be marked as a C2D message. ] + TEST_FUNCTION(IdentityMap_Receive_C2D_id_no_match_no_new_msg) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + + IDENTITY_MAP_CONFIG c1 = { "01:01:01:01:01:01", "Sensor1", "theKeyFor1" }; + IDENTITY_MAP_CONFIG c2 = { "02:02:02:02:02:02", "Sensor2", "theKeyFor2" }; + IDENTITY_MAP_CONFIG c3 = { "03:03:03:03:03:03", "Sensor3", "theKeyFor3" }; + IDENTITY_MAP_CONFIG c4 = { "04:04:04:04:04:04", "Sensor4", "theKeyFor4" }; + IDENTITY_MAP_CONFIG c5 = { "05:05:05:05:05:05", "Sensor5", "theKeyFor5" }; + IDENTITY_MAP_CONFIG c6 = { "06:06:06:06:06:06", "Sensor6", "theKeyFor6" }; + IDENTITY_MAP_CONFIG c7 = { "07:07:07:07:07:07", "Sensor7", "theKeyFor7" }; + IDENTITY_MAP_CONFIG c8 = { "08:08:08:08:08:08", "Sensor8", "theKeyFor8" }; + IDENTITY_MAP_CONFIG c9 = { "09:09:09:09:09:09", "Sensor9", "theKeyFor9" }; + VECTOR_push_back(v, &c1, 1); + VECTOR_push_back(v, &c2, 1); + VECTOR_push_back(v, &c3, 1); + VECTOR_push_back(v, &c4, 1); + VECTOR_push_back(v, &c5, 1); + VECTOR_push_back(v, &c6, 1); + VECTOR_push_back(v, &c7, 1); + VECTOR_push_back(v, &c8, 1); + VECTOR_push_back(v, &c9, 1); + auto n = theAPIS->Module_Create(bus, v); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + deviceNameProperties = "Sensor13"; + sourceProperties = GW_IOTHUB_MODULE; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + VECTOR_destroy(v); + theAPIS->Module_Destroy(n); + + } + + //Tests_SRS_IDMAP_17_048: [ If the deviceName of the message is not found in deviceToMacArray, then the message shall not be marked as a C2D message. ] + //Tests_SRS_IDMAP_17_045: [ If messageHandle properties does not contain "deviceName" property, then the message shall not be marked as a C2D message. ] + TEST_FUNCTION(IdentityMap_Receive_C2D_no_id_no_new_msg) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + + IDENTITY_MAP_CONFIG c1 = { "01:01:01:01:01:01", "Sensor1", "theKeyFor1" }; + IDENTITY_MAP_CONFIG c2 = { "02:02:02:02:02:02", "Sensor2", "theKeyFor2" }; + IDENTITY_MAP_CONFIG c3 = { "03:03:03:03:03:03", "Sensor3", "theKeyFor3" }; + IDENTITY_MAP_CONFIG c4 = { "04:04:04:04:04:04", "Sensor4", "theKeyFor4" }; + IDENTITY_MAP_CONFIG c5 = { "05:05:05:05:05:05", "Sensor5", "theKeyFor5" }; + IDENTITY_MAP_CONFIG c6 = { "06:06:06:06:06:06", "Sensor6", "theKeyFor6" }; + IDENTITY_MAP_CONFIG c7 = { "07:07:07:07:07:07", "Sensor7", "theKeyFor7" }; + IDENTITY_MAP_CONFIG c8 = { "08:08:08:08:08:08", "Sensor8", "theKeyFor8" }; + IDENTITY_MAP_CONFIG c9 = { "09:09:09:09:09:09", "Sensor9", "theKeyFor9" }; + VECTOR_push_back(v, &c1, 1); + VECTOR_push_back(v, &c2, 1); + VECTOR_push_back(v, &c3, 1); + VECTOR_push_back(v, &c4, 1); + VECTOR_push_back(v, &c5, 1); + VECTOR_push_back(v, &c6, 1); + VECTOR_push_back(v, &c7, 1); + VECTOR_push_back(v, &c8, 1); + VECTOR_push_back(v, &c9, 1); + auto n = theAPIS->Module_Create(bus, v); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + deviceNameProperties = NULL; + sourceProperties = GW_IOTHUB_MODULE; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_DEVICENAME_PROPERTY)) + .IgnoreArgument(1); + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + VECTOR_destroy(v); + theAPIS->Module_Destroy(n); + + } + + //Tests_SRS_IDMAP_17_047: [ If messageHandle property "source" is not equal to "IoTHubHttp", then the message shall not be marked as a C2D message. ] + TEST_FUNCTION(IdentityMap_Receive_no_direction_no_new_msg) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + + IDENTITY_MAP_CONFIG c1 = { "01:01:01:01:01:01", "Sensor1", "theKeyFor1" }; + IDENTITY_MAP_CONFIG c2 = { "02:02:02:02:02:02", "Sensor2", "theKeyFor2" }; + IDENTITY_MAP_CONFIG c3 = { "03:03:03:03:03:03", "Sensor3", "theKeyFor3" }; + IDENTITY_MAP_CONFIG c4 = { "04:04:04:04:04:04", "Sensor4", "theKeyFor4" }; + IDENTITY_MAP_CONFIG c5 = { "05:05:05:05:05:05", "Sensor5", "theKeyFor5" }; + IDENTITY_MAP_CONFIG c6 = { "06:06:06:06:06:06", "Sensor6", "theKeyFor6" }; + IDENTITY_MAP_CONFIG c7 = { "07:07:07:07:07:07", "Sensor7", "theKeyFor7" }; + IDENTITY_MAP_CONFIG c8 = { "08:08:08:08:08:08", "Sensor8", "theKeyFor8" }; + IDENTITY_MAP_CONFIG c9 = { "09:09:09:09:09:09", "Sensor9", "theKeyFor9" }; + VECTOR_push_back(v, &c1, 1); + VECTOR_push_back(v, &c2, 1); + VECTOR_push_back(v, &c3, 1); + VECTOR_push_back(v, &c4, 1); + VECTOR_push_back(v, &c5, 1); + VECTOR_push_back(v, &c6, 1); + VECTOR_push_back(v, &c7, 1); + VECTOR_push_back(v, &c8, 1); + VECTOR_push_back(v, &c9, 1); + auto n = theAPIS->Module_Create(bus, v); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + deviceNameProperties = "Sensor7"; + sourceProperties = NULL; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + VECTOR_destroy(v); + theAPIS->Module_Destroy(n); + + } + + //Tests_SRS_IDMAP_17_047: [ If messageHandle property "source" is not equal to "IoTHubHttp", then the message shall not be marked as a C2D message. ] + TEST_FUNCTION(IdentityMap_Receive_mapping_ignore_mapping_msg) + { + ///Arrange + CIdentitymapMocks mocks; + const MODULE_APIS* theAPIS = Module_GetAPIS(); + + unsigned char fake; + MESSAGE_BUS_HANDLE bus = (MESSAGE_BUS_HANDLE)&fake; + VECTOR_HANDLE v = VECTOR_create(sizeof(IDENTITY_MAP_CONFIG)); + + IDENTITY_MAP_CONFIG c1 = { "01:01:01:01:01:01", "Sensor1", "theKeyFor1" }; + IDENTITY_MAP_CONFIG c2 = { "02:02:02:02:02:02", "Sensor2", "theKeyFor2" }; + IDENTITY_MAP_CONFIG c3 = { "03:03:03:03:03:03", "Sensor3", "theKeyFor3" }; + IDENTITY_MAP_CONFIG c4 = { "04:04:04:04:04:04", "Sensor4", "theKeyFor4" }; + IDENTITY_MAP_CONFIG c5 = { "05:05:05:05:05:05", "Sensor5", "theKeyFor5" }; + IDENTITY_MAP_CONFIG c6 = { "06:06:06:06:06:06", "Sensor6", "theKeyFor6" }; + IDENTITY_MAP_CONFIG c7 = { "07:07:07:07:07:07", "Sensor7", "theKeyFor7" }; + IDENTITY_MAP_CONFIG c8 = { "08:08:08:08:08:08", "Sensor8", "theKeyFor8" }; + IDENTITY_MAP_CONFIG c9 = { "09:09:09:09:09:09", "Sensor9", "theKeyFor9" }; + VECTOR_push_back(v, &c1, 1); + VECTOR_push_back(v, &c2, 1); + VECTOR_push_back(v, &c3, 1); + VECTOR_push_back(v, &c4, 1); + VECTOR_push_back(v, &c5, 1); + VECTOR_push_back(v, &c6, 1); + VECTOR_push_back(v, &c7, 1); + VECTOR_push_back(v, &c8, 1); + VECTOR_push_back(v, &c9, 1); + auto n = theAPIS->Module_Create(bus, v); + + MESSAGE_CONFIG cfg = { 1, &fake, (MAP_HANDLE)&fake }; + auto m = Message_Create(&cfg); + + macAddressProperties = "07:07:07:07:07:07"; + deviceNameProperties = "Sensor7"; + deviceKeyProperties = "theKeyFor7"; + sourceProperties = GW_IDMAP_MODULE; + + mocks.ResetAllCalls(); + + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(m)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Create(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)).IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(IGNORED_PTR_ARG, GW_SOURCE_PROPERTY)) + .IgnoreArgument(1); + + + ///Act + theAPIS->Module_Receive(n, m); + + ///Assert + mocks.AssertActualAndExpectedCalls(); + + ///Ablution + Message_Destroy(m); + VECTOR_destroy(v); + theAPIS->Module_Destroy(n); + + } + // + +END_TEST_SUITE(identitymap_unittests) diff --git a/modules/identitymap/tests/idmap_unittests/main.c b/modules/identitymap/tests/idmap_unittests/main.c new file mode 100644 index 00000000..49943fee --- /dev/null +++ b/modules/identitymap/tests/idmap_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(identitymap_unittests, failedTestCount); + return failedTestCount; +} diff --git a/modules/iothubhttp/CMakeLists.txt b/modules/iothubhttp/CMakeLists.txt new file mode 100644 index 00000000..df513d25 --- /dev/null +++ b/modules/iothubhttp/CMakeLists.txt @@ -0,0 +1,86 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) +#this is CMakeLists for the iothubhttp module + +set(iothubhttp_sources + ./src/iothubhttp.c +) + +set(iothubhttp_headers + ./inc/iothubhttp.h +) + +set(iothubhttp_hl_sources + ./src/iothubhttp_hl.c +) + +set(iothubhttp_hl_headers + ./inc/iothubhttp_hl.h +) + +include_directories(./inc) +include_directories(${GW_INC}) +include_directories(${IOTHUB_CLIENT_INC_FOLDER}) + +#this builds the IoTHubHTTP dynamic library +add_library(iothubhttp MODULE + ${iothubhttp_sources} + ${iothubhttp_headers} +) +target_link_libraries(iothubhttp + gateway + iothub_client + iothub_client_http_transport +) + +#this builds the IoTHubHTTP static library +add_library(iothubhttp_static + ${iothubhttp_sources} + ${iothubhttp_headers} +) +target_compile_definitions(iothubhttp_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(iothubhttp_static + gateway + iothub_client + iothub_client_http_transport +) + +#this builds the IoTHubHTTP_HL dynamic library (by default it uses IoTHub Module linked statically) +add_library(iothubhttp_hl MODULE + ${iothubhttp_hl_sources} + ${iothubhttp_hl_headers} +) +target_link_libraries(iothubhttp_hl + iothubhttp_static +) + +#this builds the IoTHubHTTP_HL static library (by default it uses IoTHub Module linked statically) +add_library(iothubhttp_hl_static + ${iothubhttp_hl_sources} + ${iothubhttp_hl_headers} +) +target_compile_definitions(iothubhttp_hl_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(iothubhttp_hl_static + iothubhttp_static +) + +linkSharedUtil(iothubhttp) +linkSharedUtil(iothubhttp_static) +linkSharedUtil(iothubhttp_hl) +linkSharedUtil(iothubhttp_hl_static) + +linkHttp(iothubhttp) +linkHttp(iothubhttp_static) +linkHttp(iothubhttp_hl) +linkHttp(iothubhttp_hl_static) + +add_module_to_solution(iothubhttp) + +add_subdirectory(tests) + +if(install_executables) + install(TARGETS iothubhttp LIBRARY DESTINATION lib) + install(TARGETS iothubhttp_hl LIBRARY DESTINATION lib) +endif() diff --git a/modules/iothubhttp/README.md b/modules/iothubhttp/README.md new file mode 100644 index 00000000..5884dc3b --- /dev/null +++ b/modules/iothubhttp/README.md @@ -0,0 +1 @@ +# IoT Hub Module (Over HTTP) \ No newline at end of file diff --git a/modules/iothubhttp/devdoc/iothubhttp.md b/modules/iothubhttp/devdoc/iothubhttp.md new file mode 100644 index 00000000..5d883208 --- /dev/null +++ b/modules/iothubhttp/devdoc/iothubhttp.md @@ -0,0 +1,155 @@ +IOTHUB HTTP MODULE +=========== + +High level design +----------------- + +### Overview + +#### Publishing events +This module ensured IoTHub connectivity for all the mapped devices on the bus. The module identifies the messages that it needs to process +by the following properties that must exist: + +>| PropertyName | Description | +>|--------------|------------------------------------------------------------------------------| +>| source | The module only processes message that have source set to "mapping". | +>| deviceName | The deviceName as registered with IoTHub | +>| deviceKey | The key as registered with IoTHub | + +The module shall dynamically create instances of IoTHubClient (one per each device). The module shall use HTTP +protocol for connections. + +#### Receiving IoT Hub messages +Upon reception of a message from the IoTHub, this module will publish a message to the bus. The published message will have the following properties: +>| PropertyName | Description | +>| ------------- | ----------------------------------------------------------------------------------- | +>| source | "IoTHubHTTP" | +>| deviceName | The receiver's deviceName, as registered with IoTHub | +>| * (all other) | All other properties of the received message will be added to the published message | + +The body of the published message will be the body of the received message. + + +####Additional data types +```C +typedef struct IOTHUBHTTP_CONFIG_TAG +{ + const char* IoTHubName; /*the name of the IoTHub*/ + const char* IoTHubSuffix; /*the suffix used in generating the host name*/ +}IOTHUBHTTP_CONFIG; /*this needs to be passed to the Module_Create function*/ +``` + +###IoTHubHttp_Create +```C +MODULE_HANDLE IoTHubHttp_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +``` +Creates a new IoTHubHttp instance. configuration is a pointer to a `IOTHUBHTTP_CONFIG`. + +**SRS_IOTHUBHTTP_02_001: [**If `busHandle` is NULL then `IoTHubHttp_Create` shall fail and return NULL.**]** +**SRS_IOTHUBHTTP_02_002: [**If `configuration` is NULL then `IoTHubHttp_Create` shall fail and return NULL.**]** +**SRS_IOTHUBHTTP_02_003: [**If `configuration->IoTHubName` is NULL then `IoTHubHttp_Create` shall and return NULL.**]** +**SRS_IOTHUBHTTP_02_004: [**If `configuration->IoTHubSuffix` is NULL then `IoTHubHttp_Create` shall fail and return NULL.**]** + +IoTHubHttp shall maintain a shared transport for all devices. + +**SRS_IOTHUBHTTP_17_001: [** `IoTHubHttp_Create` shall create a shared transport by calling `IoTHubTransport_Create`. **]** + +**SRS_IOTHUBHTTP_17_002: [** If creating the shared transport fails, `IoTHubHttp_Create` shall fail and return `NULL`. **]** + +IoTHubHttp shall name the triplet of deviceName, deviceKey and IOTHUB_CLIENT_HANDLE `PERSONALITY`. + +**SRS_IOTHUBHTTP_02_006: [**`IoTHubHttp_Create` shall create an empty `VECTOR` containing pointers to `PERSONALITY`s.**]** + +**SRS_IOTHUBHTTP_02_007: [**If creating the `VECTOR` fails +then `IoTHubHttp_Create` shall fail and return NULL.**]** +**SRS_IOTHUBHTTP_02_028: [**`IoTHubHttp_Create` shall create a copy of `configuration->IoTHubName`.**]** +**SRS_IOTHUBHTTP_02_029: [**`IoTHubHttp_Create` shall create a copy of `configuration->IoTHubSuffix`.**]** +**SRS_IOTHUBHTTP_17_004: [** `IoTHubHttp_Create` shall store the busHandle. **]** +**SRS_IOTHUBHTTP_02_027: [**When `IoTHubHttp_Create` encounters an internal failure it shall fail and return NULL.**]** + +**SRS_IOTHUBHTTP_02_008: [**Otherwise, `IoTHubHttp_Create` shall return a non-NULL handle.**]** + +###IoTHubHttp_Receive +```C +MODULE_HANDLE IoTHubHttp_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); +``` +**SRS_IOTHUBHTTP_02_009: [**If `moduleHandle` or `messageHandle` is NULL then `IoTHubHttp_Receive` shall do nothing.**]** +**SRS_IOTHUBHTTP_02_010: [**If message properties do not contain a property called "source" having the value set to "mapping" then `IoTHubHttp_Receive` shall do nothing.**]** +**SRS_IOTHUBHTTP_02_011: [**If message properties do not contain a property called "deviceName" having a non-NULL value then `IoTHubHttp_Receive` shall do nothing.**]** +**SRS_IOTHUBHTTP_02_012: [**If message properties do not contain a property called "deviceKey" having a non-NULL value then `IoTHubHttp_Receive` shall do nothing.**]** + +**SRS_IOTHUBHTTP_02_013: [**If the deviceName does not exist in the `PERSONALITY` collection then `IoTHubHttp_Receive` shall create a new +`PERSONALITY` containing `deviceName`, `deviceKey` and an `IOTHUB_CLIENT_HANDLE` (by a call to `IoTHubClient_CreateWithTransport`).**]** +**SRS_IOTHUBHTTP_17_003: [** If a new `PERSONALITY` is created, then the IoTHubClient will be set to receive messages, by calling `IoTHubClient_SetMessageCallback` with callback function `IoTHubHttp_ReceiveMessageCallback` and `PERSONALITY` as context.**]** +**SRS_IOTHUBHTTP_02_014: [**If creating the `PERSONALITY` fails then `IoTHubHttp_Receive` shall return.**]** +**SRS_IOTHUBHTTP_02_016: [**If adding the new triplet fails, then `IoTHubClient_Create` shall return.**]** +**SRS_IOTHUBHTTP_02_017: [**If the deviceName exists in the `PERSONALITY` collection then `IoTHubHttp_Receive` shall not +create a new IOTHUB_CLIENT_HANDLE.**]** + +**SRS_IOTHUBHTTP_02_018: [**`IoTHubHttp_Receive` shall create a new IOTHUB_MESSAGE_HANDLE having the same content as the `messageHandle` and +same properties with the exception of deviceName and deviceKey properties.**]** + +**SRS_IOTHUBHTTP_02_019: [**If creating the IOTHUB_MESSAGE_HANDLE fails, then `IoTHubHttp_Receive` shall return.**]** + +**SRS_IOTHUBHTTP_02_020: [**`IoTHubHttp_Receive` shall call IoTHubClient_SendEventAsync passing the IOTHUB_MESSAGE_HANDLE.**]** +**SRS_IOTHUBHTTP_02_021: [**If `IoTHubClient_SendEventAsync` fails then `IoTHubHttp_Receive` shall return.**]** + +**SRS_IOTHUBHTTP_02_022: [**`IoTHubHttp_Receive` shall return.**]** + + +### IoTHubHttp_ReceiveMessageCallback +```c +IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubHttp_ReceiveMessageCallback(IOTHUB_MESSAGE_HANDLE msg, void* userContextCallback) +``` + +**SRS_IOTHUBHTTP_17_005: [** If `userContextCallback` is `NULL`, then `IoTHubHttp_ReceiveMessageCallback` shall return `IOTHUBMESSAGE_ABANDONED`. **]** + +**SRS_IOTHUBHTTP_17_006: [** If Message Content type is `IOTHUBMESSAGE_UNKNOWN`, then `IoTHubHttp_ReceiveMessageCallback` shall return `IOTHUBMESSAGE_ABANDONED`. **]** + +**SRS_IOTHUBHTTP_17_007: [** `IoTHubHttp_ReceiveMessageCallback` shall get properties from message by calling `IoTHubMessage_Properties`. **]** + +**SRS_IOTHUBHTTP_17_008: [** If message properties are `NULL`, `IoTHubHttp_ReceiveMessageCallback` shall return `IOTHUBMESSAGE_ABANDONED`. **]** + +**SRS_IOTHUBHTTP_17_009: [** `IoTHubHttp_ReceiveMessageCallback` shall define a property "source" as "IoTHubHTTP". **]** + +**SRS_IOTHUBHTTP_17_010: [** `IoTHubHttp_ReceiveMessageCallback` shall define a property "deviceName" as the `PERSONALITY`'s deviceName. **]** + +**SRS_IOTHUBHTTP_17_011: [** `IoTHubHttp_ReceiveMessageCallback` shall combine message properties with the "source" and "deviceName" properties. **]** + +**SRS_IOTHUBHTTP_17_022: [** If message properties fail to combine, `IoTHubHttp_ReceiveMessageCallback` shall return `IOTHUBMESSAGE_ABANDONED`. **]** + +**SRS_IOTHUBHTTP_17_013: [** If Message content type is `IOTHUBMESSAGE_BYTEARRAY`, `IoTHubHttp_ReceiveMessageCallback` shall get the size and buffer from the results of `IoTHubMessage_GetByteArray`. **]** + +**SRS_IOTHUBHTTP_17_023: [** If `IoTHubMessage_GetByteArray` fails, `IoTHubHttp_ReceiveMessageCallback` shall return `IOTHUBMESSAGE_ABANDONED`. **]** + +**SRS_IOTHUBHTTP_17_014: [** If Message content type is `IOTHUBMESSAGE_STRING`, `IoTHubHttp_ReceiveMessageCallback` shall get the buffer from results of `IoTHubMessage_GetString`. **]** + +**SRS_IOTHUBHTTP_17_015: [** If Message content type is `IOTHUBMESSAGE_STRING`, `IoTHubHttp_ReceiveMessageCallback` shall get the buffer size from the string length. **]** + +**SRS_IOTHUBHTTP_17_016: [** `IoTHubHttp_ReceiveMessageCallback` shall create a new message from combined properties, the size and buffer. **]** + +**SRS_IOTHUBHTTP_17_017: [** If the message fails to create, `IoTHubHttp_ReceiveMessageCallback` shall return `IOTHUBMESSAGE_REJECTED`. **]** + +**SRS_IOTHUBHTTP_17_018: [** `IoTHubHttp_ReceiveMessageCallback` shall call `Bus_Publish` with the new message and the `busHandle`. **]** + +**SRS_IOTHUBHTTP_17_019: [** If the message fails to publish, `IoTHubHttp_ReceiveMessageCallback` shall return `IOTHUBMESSAGE_REJECTED`. **]** + +**SRS_IOTHUBHTTP_17_020: [** `IoTHubHttp_ReceiveMessageCallback` shall destroy all resources it creates. **]** + +**SRS_IOTHUBHTTP_17_021: [** Upon success, `IoTHubHttp_ReceiveMessageCallback` shall return `IOTHUBMESSAGE_ACCEPTED`. **]** + + +###IoTHubHttp_Destroy +```C +void IoTHubHttp_Destroy(MODULE_HANDLE moduleHandle); +``` +**SRS_IOTHUBHTTP_02_023: [**If `moduleHandle` is NULL then `IoTHubHttp_Destroy` shall return.**]** +**SRS_IOTHUBHTTP_02_024: [**Otherwise `IoTHubHttp_Destroy` shall free all used resources.**]** + +###Module_GetAPIs +```C +extern const MODULE_APIS* Module_GetAPIS(void); +``` + +**SRS_IOTHUBHTTP_02_025: [**`Module_GetAPIS` shall return a non-NULL pointer.**]** +**SRS_IOTHUBHTTP_02_026: [**The MODULE_APIS structure shall have non-NULL `Module_Create`, `Module_Destroy`, and `Module_Receive` fields.**]** \ No newline at end of file diff --git a/modules/iothubhttp/devdoc/iothubhttp_hl.md b/modules/iothubhttp/devdoc/iothubhttp_hl.md new file mode 100644 index 00000000..5fe3aa32 --- /dev/null +++ b/modules/iothubhttp/devdoc/iothubhttp_hl.md @@ -0,0 +1,81 @@ +IOTHUB HTTP HL MODULE +=========== + +High level design +----------------- + +### References +[IOTHUB HTTP MODULE](./iothubhttpmodule.md) + +[json](http://www.json.org) + +[gateway](../../../../devdoc/gateway_requirements.md) + +### Overview + +This module adapts the existing lower layer IoTHubHttp module for use with the Gateway +HL library. It is mostly a passthrough to the existing module, with a specialized +create function to interpret the serialized JSON module arguments. + +### Expected Arguments +```json +{ + "IoTHubName" : "", + "IoTHubSuffix" : "" +} +``` + + +###IoTHubHttp_HL_Create +```C +MODULE_HANDLE IoTHubHttp_HL_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +``` +Creates a new IoTHubHttp_HL instance. `configuration` is a pointer to a char* +of the serialzed JSON object, as supplied by `Gateway_Create_From_JSON`. + +**SRS_IOTHUBHTTP_HL_17_001: [**If `busHandle` is NULL then `IoTHubHttp_HL_Create` +shall fail and return NULL.**]** + +**SRS_IOTHUBHTTP_HL_17_002: [**If `configuration` is NULL then +`IoTHubHttp_HL_Create` shall fail and return NULL.**]** + +**SRS_IOTHUBHTTP_HL_17_003: [** If `configuration` is not a JSON string, then `IoTHubHttp_HL_Create` shall +fail and return NULL. **]** + +**SRS_IOTHUBHTTP_HL_17_004: [** `IoTHubHttp_HL_Create` shall parse the `configuration` as a JSON string. **]** + +**SRS_IOTHUBHTTP_HL_17_005: [** If parsing configuration fails, `IoTHubHttp_HL_Create` shall fail and return +NULL. **]** + +**SRS_IOTHUBHTTP_HL_17_006: [** If the JSON object does not contain a value named "IoTHubName" then `IoTHubHttp_HL_Create` shall fail and return NULL. **]** + +**SRS_IOTHUBHTTP_HL_17_007: [** If the JSON object does not contain a value named "IoTHubSuffix" then `IoTHubHttp_HL_Create` shall fail and return NULL. **]** + +**SRS_IOTHUBHTTP_HL_17_008: [** `IoTHubHttp_HL_Create` shall invoke the IoTHubHttp Module's create, using the busHandle, IotHubName, and IoTHubSuffix. **]** + +**SRS_IOTHUBHTTP_HL_17_009: [** When the lower layer IoTHubHttp create succeeds, `IoTHubHttp_HL_Create` shall succeed and return a non-NULL value. **]** + +**SRS_IOTHUBHTTP_HL_17_010: [** If the lower layer IoTHubHttp create fails, `IoTHubHttp_HL_Create` shall fail and return NULL. **]** + +###IoTHubHttp_HL_Receive +```C +MODULE_HANDLE IoTHubHttp_HL_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); +``` + +**SRS_IOTHUBHTTP_HL_17_011: [** `IoTHubHttp_HL_Receive` shall pass the received parameters to the underlying IoTHubHttp receive function. **]** + +###IoTHubHttp_HL_Destroy +```C +void IoTHubHttp_HL_Destroy(MODULE_HANDLE moduleHandle); +``` +**SRS_IOTHUBHTTP_HL_17_012: [** `IoTHubHttp_HL_Destroy` shall free all used resources. **]** + + +###Module_GetAPIs +```C +extern const MODULE_APIS* Module_GetAPIS(void); +``` + +**SRS_IOTHUBHTTP_HL_17_013: [** `Module_GetAPIS` shall return a non-NULL pointer. **]** + +**SRS_IOTHUBHTTP_HL_17_014: [** The MODULE_APIS structure shall have non-NULL `Module_Create`, `Module_Destroy`, and `Module_Receive` fields. **]** \ No newline at end of file diff --git a/modules/iothubhttp/inc/iothubhttp.h b/modules/iothubhttp/inc/iothubhttp.h new file mode 100644 index 00000000..78ab117a --- /dev/null +++ b/modules/iothubhttp/inc/iothubhttp.h @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBHTTP_H +#define IOTHUBHTTP_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct IOTHUBHTTP_CONFIG_TAG +{ + const char* IoTHubName; + const char* IoTHubSuffix; +}IOTHUBHTTP_CONFIG; /*this needs to be passed to the Module_Create function*/ + +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE)(void); + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBHTTP_H*/ diff --git a/modules/iothubhttp/inc/iothubhttp_hl.h b/modules/iothubhttp/inc/iothubhttp_hl.h new file mode 100644 index 00000000..3b74e240 --- /dev/null +++ b/modules/iothubhttp/inc/iothubhttp_hl.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef IOTHUBHTTP_HL_H +#define IOTHUBHTTP_HL_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE_HL)(void); + +#ifdef __cplusplus +} +#endif + +#endif /*IOTHUBHTTP_HL_H*/ diff --git a/modules/iothubhttp/src/iothubhttp.c b/modules/iothubhttp/src/iothubhttp.c new file mode 100644 index 00000000..5dc25473 --- /dev/null +++ b/modules/iothubhttp/src/iothubhttp.c @@ -0,0 +1,538 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include "azure_c_shared_utility/gballoc.h" + +#include "iothubhttp.h" +#include "iothub_client.h" +#include "iothubtransport.h" +#include "iothubtransporthttp.h" +#include "iothub_message.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/strings.h" +#include "messageproperties.h" +#include "message_bus.h" + +typedef struct PERSONALITY_TAG +{ + STRING_HANDLE deviceName; + STRING_HANDLE deviceKey; + IOTHUB_CLIENT_HANDLE iothubHandle; + MESSAGE_BUS_HANDLE busHandle; +}PERSONALITY; + +typedef PERSONALITY* PERSONALITY_PTR; + +typedef struct IOTHUBHTTP_HANDLE_DATA_TAG +{ + VECTOR_HANDLE personalities; /*holds PERSONALITYs*/ + STRING_HANDLE IoTHubName; + STRING_HANDLE IoTHubSuffix; + TRANSPORT_HANDLE transportHandle; + MESSAGE_BUS_HANDLE busHandle; +}IOTHUBHTTP_HANDLE_DATA; + +#define SOURCE "source" +#define MAPPING "mapping" +#define DEVICENAME "deviceName" +#define DEVICEKEY "deviceKey" + +static MODULE_HANDLE IoTHubHttp_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + IOTHUBHTTP_HANDLE_DATA *result; + /*Codes_SRS_IOTHUBHTTP_02_001: [If busHandle is NULL then IoTHubHttp_Create shall fail and return NULL.]*/ + /*Codes_SRS_IOTHUBHTTP_02_002: [If configuration is NULL then IoTHubHttp_Create shall fail and return NULL.]*/ + /*Codes_SRS_IOTHUBHTTP_02_003: [If configuration->IoTHubName is NULL then IoTHubHttp_Create shall and return NULL.]*/ + /*Codes_SRS_IOTHUBHTTP_02_004: [If configuration->IoTHubSuffix is NULL then IoTHubHttp_Create shall fail and return NULL.]*/ + if ( + (busHandle == NULL) || + (configuration == NULL) || + (((const IOTHUBHTTP_CONFIG*)configuration)->IoTHubName == NULL) || + (((const IOTHUBHTTP_CONFIG*)configuration)->IoTHubSuffix == NULL) + ) + { + LogError("invalid arg busHandle=%p, configuration=%p, IoTHubName=%s IoTHubSuffix=%s", busHandle, configuration, (configuration!=NULL)?((const IOTHUBHTTP_CONFIG*)configuration)->IoTHubName:"undefined behavior", (configuration != NULL) ? ((const IOTHUBHTTP_CONFIG*)configuration)->IoTHubSuffix : "undefined behavior"); + result = NULL; + } + else + { + result = malloc(sizeof(IOTHUBHTTP_HANDLE_DATA)); + /*Codes_SRS_IOTHUBHTTP_02_027: [When IoTHubHttp_Create encounters an internal failure it shall fail and return NULL.]*/ + if (result == NULL) + { + LogError("malloc returned NULL"); + /*return as is*/ + } + else + { + /*Codes_SRS_IOTHUBHTTP_02_006: [IoTHubHttp_Create shall create an empty VECTOR containing pointers to PERSONALITYs.]*/ + result->personalities = VECTOR_create(sizeof(PERSONALITY_PTR)); + if (result->personalities == NULL) + { + /*Codes_SRS_IOTHUBHTTP_02_007: [If creating the VECTOR fails then IoTHubHttp_Create shall fail and return NULL.]*/ + free(result); + result = NULL; + LogError("VECTOR_create returned NULL"); + } + else + { + /*Codes_SRS_IOTHUBHTTP_17_001: [ IoTHubHttp_Create shall create a shared transport by calling IoTHubTransport_Create. ]*/ + result->transportHandle = IoTHubTransport_Create(HTTP_Protocol, ((const IOTHUBHTTP_CONFIG*)configuration)->IoTHubName, ((const IOTHUBHTTP_CONFIG*)configuration)->IoTHubSuffix); + if (result->transportHandle == NULL) + { + /*Codes_SRS_IOTHUBHTTP_17_002: [ If creating the shared transport fails, IoTHubHttp_Create shall fail and return NULL. ]*/ + VECTOR_destroy(result->personalities); + free(result); + result = NULL; + LogError("VECTOR_create returned NULL"); + } + else + { + /*Codes_SRS_IOTHUBHTTP_02_028: [IoTHubHttp_Create shall create a copy of configuration->IoTHubSuffix.]*/ + /*Codes_SRS_IOTHUBHTTP_02_029: [IoTHubHttp_Create shall create a copy of configuration->IoTHubName.]*/ + if ((result->IoTHubName = STRING_construct(((const IOTHUBHTTP_CONFIG*)configuration)->IoTHubName)) == NULL) + { + IoTHubTransport_Destroy(result->transportHandle); + VECTOR_destroy(result->personalities); + free(result); + result = NULL; + } + else if ((result->IoTHubSuffix = STRING_construct(((const IOTHUBHTTP_CONFIG*)configuration)->IoTHubSuffix)) == NULL) + { + STRING_delete(result->IoTHubName); + IoTHubTransport_Destroy(result->transportHandle); + VECTOR_destroy(result->personalities); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBHTTP_17_004: [ IoTHubHttp_Create shall store the busHandle. ]*/ + result->busHandle = busHandle; + /*Codes_SRS_IOTHUBHTTP_02_008: [Otherwise, IoTHubHttp_Create shall return a non-NULL handle.]*/ + } + } + } + } + } + return result; +} + +static void IoTHubHttp_Destroy(MODULE_HANDLE moduleHandle) +{ + /*Codes_SRS_IOTHUBHTTP_02_023: [If moduleHandle is NULL then IoTHubHttp_Destroy shall return.]*/ + if (moduleHandle == NULL) + { + LogError("moduleHandle parameter was NULL"); + } + else + { + /*Codes_SRS_IOTHUBHTTP_02_024: [Otherwise IoTHubHttp_Destroy shall free all used resources.]*/ + IOTHUBHTTP_HANDLE_DATA * handleData = moduleHandle; + size_t vectorSize = VECTOR_size(handleData->personalities); + for (size_t i = 0; i < vectorSize; i++) + { + PERSONALITY_PTR* personality = VECTOR_element(handleData->personalities, i); + STRING_delete((*personality)->deviceKey); + STRING_delete((*personality)->deviceName); + IoTHubClient_Destroy((*personality)->iothubHandle); + free(*personality); + } + IoTHubTransport_Destroy(handleData->transportHandle); + VECTOR_destroy(handleData->personalities); + STRING_delete(handleData->IoTHubName); + STRING_delete(handleData->IoTHubSuffix); + free(handleData); + } +} + +static bool lookup_DeviceName(const void* element, const void* value) +{ + return (strcmp(STRING_c_str((*(PERSONALITY_PTR*)element)->deviceName), value) == 0); +} + +static IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubHttp_ReceiveMessageCallback(IOTHUB_MESSAGE_HANDLE msg, void* userContextCallback) +{ + IOTHUBMESSAGE_DISPOSITION_RESULT result; + if (userContextCallback == NULL) + { + /*Codes_SRS_IOTHUBHTTP_17_005: [ If userContextCallback is NULL, then IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ]*/ + LogError("No context to associate message"); + result = IOTHUBMESSAGE_ABANDONED; + } + else + { + PERSONALITY_PTR personality = (PERSONALITY_PTR)userContextCallback; + IOTHUBMESSAGE_CONTENT_TYPE msgContentType = IoTHubMessage_GetContentType(msg); + if (msgContentType == IOTHUBMESSAGE_UNKNOWN) + { + /*Codes_SRS_IOTHUBHTTP_17_006: [ If Message Content type is IOTHUBMESSAGE_UNKNOWN, then IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ]*/ + LogError("Message content type is unknown"); + result = IOTHUBMESSAGE_ABANDONED; + } + else + { + /* Handle message content */ + MESSAGE_CONFIG newMessageConfig; + IOTHUB_MESSAGE_RESULT msgResult; + if (msgContentType == IOTHUBMESSAGE_STRING) + { + /*Codes_SRS_IOTHUBHTTP_17_014: [ If Message content type is IOTHUBMESSAGE_STRING, IoTHubHttp_ReceiveMessageCallback shall get the buffer from results of IoTHubMessage_GetString. ]*/ + const char* sourceStr = IoTHubMessage_GetString(msg); + newMessageConfig.source = (const unsigned char*)sourceStr; + /*Codes_SRS_IOTHUBHTTP_17_015: [ If Message content type is IOTHUBMESSAGE_STRING, IoTHubHttp_ReceiveMessageCallback shall get the buffer size from the string length. ]*/ + newMessageConfig.size = strlen(sourceStr); + msgResult = IOTHUB_MESSAGE_OK; + } + else + { + /* content type is byte array */ + /*Codes_SRS_IOTHUBHTTP_17_013: [ If Message content type is IOTHUBMESSAGE_BYTEARRAY, IoTHubHttp_ReceiveMessageCallback shall get the size and buffer from the results of IoTHubMessage_GetByteArray. ]*/ + msgResult = IoTHubMessage_GetByteArray(msg, &(newMessageConfig.source), &(newMessageConfig.size)); + } + if (msgResult != IOTHUB_MESSAGE_OK) + { + /*Codes_SRS_IOTHUBHTTP_17_023: [ If IoTHubMessage_GetByteArray fails, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ]*/ + LogError("Failed to get message content"); + result = IOTHUBMESSAGE_ABANDONED; + } + else + { + /* Now, handle message properties. */ + MAP_HANDLE newProperties; + /*Codes_SRS_IOTHUBHTTP_17_007: [ IoTHubHttp_ReceiveMessageCallback shall get properties from message by calling IoTHubMessage_Properties. */ + newProperties = IoTHubMessage_Properties(msg); + if (newProperties == NULL) + { + /*Codes_SRS_IOTHUBHTTP_17_008: [ If message properties are NULL, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ]*/ + LogError("No Properties in IoTHub Message"); + result = IOTHUBMESSAGE_ABANDONED; + } + else + { + /*Codes_SRS_IOTHUBHTTP_17_009: [ IoTHubHttp_ReceiveMessageCallback shall define a property "source" as "IoTHubHTTP". ]*/ + /*Codes_SRS_IOTHUBHTTP_17_010: [ IoTHubHttp_ReceiveMessageCallback shall define a property "deviceName" as the PERSONALITY's deviceName. ]*/ + /*Codes_SRS_IOTHUBHTTP_17_011: [ IoTHubHttp_ReceiveMessageCallback shall combine message properties with the "source" and "deviceName" properties. ]*/ + if (Map_Add(newProperties, GW_SOURCE_PROPERTY, GW_IOTHUB_MODULE) != MAP_OK) + { + /*Codes_SRS_IOTHUBHTTP_17_022: [ If message properties fail to combine, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ]*/ + LogError("Property [%s] did not add properly", GW_SOURCE_PROPERTY); + result = IOTHUBMESSAGE_ABANDONED; + } + else if (Map_Add(newProperties, GW_DEVICENAME_PROPERTY, STRING_c_str(personality->deviceName)) != MAP_OK) + { + /*Codes_SRS_IOTHUBHTTP_17_022: [ If message properties fail to combine, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ]*/ + LogError("Property [%s] did not add properly", GW_DEVICENAME_PROPERTY); + result = IOTHUBMESSAGE_ABANDONED; + } + else + { + /*Codes_SRS_IOTHUBHTTP_17_016: [ IoTHubHttp_ReceiveMessageCallback shall create a new message from combined properties, the size and buffer. ]*/ + newMessageConfig.sourceProperties = newProperties; + MESSAGE_HANDLE gatewayMsg = Message_Create(&newMessageConfig); + if (gatewayMsg == NULL) + { + /*Codes_SRS_IOTHUBHTTP_17_017: [ If the message fails to create, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_REJECTED. ]*/ + LogError("Failed to create gateway message"); + result = IOTHUBMESSAGE_REJECTED; +} + else + { + /*Codes_SRS_IOTHUBHTTP_17_018: [ IoTHubHttp_ReceiveMessageCallback shall call MessageBus_Publish with the new message and the busHandle. ]*/ + if (MessageBus_Publish(personality->busHandle, gatewayMsg) != MESSAGE_BUS_OK) + { + /*Codes_SRS_IOTHUBHTTP_17_019: [ If the message fails to publish, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_REJECTED. ]*/ + LogError("Failed to publish gateway message"); + result = IOTHUBMESSAGE_REJECTED; + } + else + { + /*Codes_SRS_IOTHUBHTTP_17_021: [ Upon success, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ACCEPTED. ]*/ + result = IOTHUBMESSAGE_ACCEPTED; + } + /*Codes_SRS_IOTHUBHTTP_17_020: [ IoTHubHttp_ReceiveMessageCallback shall destroy all resources it creates. ]*/ + Message_Destroy(gatewayMsg); + } + } + } + } + } + } + return result; +} + +/*returns non-null if PERSONALITY has been properly populated*/ +static PERSONALITY_PTR PERSONALITY_create(const char* deviceName, const char* deviceKey, IOTHUBHTTP_HANDLE_DATA* moduleHandleData) +{ + PERSONALITY_PTR result = (PERSONALITY_PTR)malloc(sizeof(PERSONALITY)); + if (result == NULL) + { + LogError("unable to allocate a personality for the device %s", deviceName); + } + else + { + if ((result->deviceName = STRING_construct(deviceName)) == NULL) + { + LogError("unable to STRING_construct"); + free(result); + result = NULL; + } + else if ((result->deviceKey = STRING_construct(deviceKey)) == NULL) + { + LogError("unable to STRING_construct"); + STRING_delete(result->deviceName); + free(result); + result = NULL; + } + else + { + IOTHUB_CLIENT_CONFIG temp; + temp.deviceId = deviceName; + temp.deviceKey = deviceKey; + temp.iotHubName = STRING_c_str(moduleHandleData->IoTHubName); + temp.iotHubSuffix = STRING_c_str(moduleHandleData->IoTHubSuffix); + temp.protocol = HTTP_Protocol; + temp.protocolGatewayHostName = NULL; + if ((result->iothubHandle = IoTHubClient_CreateWithTransport(moduleHandleData->transportHandle, &temp)) == NULL) + { + LogError("unable to IoTHubClient_CreateWithTransport"); + STRING_delete(result->deviceName); + STRING_delete(result->deviceKey); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBHTTP_17_003: [ If a new PERSONALITY is created, then the IoTHubClient will be set to receive messages, by calling IoTHubClient_SetMessageCallback with callback function IoTHubHttp_ReceiveMessageCallback and PERSONALITY as context.]*/ + if (IoTHubClient_SetMessageCallback(result->iothubHandle, IoTHubHttp_ReceiveMessageCallback, result) != IOTHUB_CLIENT_OK) + { + LogError("unable to IoTHubClient_SetMessageCallback"); + IoTHubClient_Destroy(result->iothubHandle); + STRING_delete(result->deviceName); + STRING_delete(result->deviceKey); + free(result); + result = NULL; + } + else + { + /*it is all fine*/ + result->busHandle = moduleHandleData->busHandle; + } + } + } + } + return result; +} + +static void PERSONALITY_destroy(PERSONALITY* personality) +{ + STRING_delete(personality->deviceName); + STRING_delete(personality->deviceKey); + IoTHubClient_Destroy(personality->iothubHandle); +} + +static PERSONALITY* PERSONALITY_find_or_create(IOTHUBHTTP_HANDLE_DATA* moduleHandleData, const char* deviceName, const char* deviceKey) +{ + /*Codes_SRS_IOTHUBHTTP_02_017: [If the deviceName exists in the PERSONALITY collection then IoTHubHttp_Receive shall not create a new IOTHUB_CLIENT_HANDLE.]*/ + PERSONALITY* result; + PERSONALITY_PTR* resultPtr = VECTOR_find_if(moduleHandleData->personalities, lookup_DeviceName, deviceName); + if (resultPtr == NULL) + { + /*a new device has arrived!*/ + PERSONALITY_PTR personality; + if ((personality = PERSONALITY_create(deviceName, deviceKey, moduleHandleData)) == NULL) + { + LogError("unable to create a personality for the device %s", deviceName); + result = NULL; + } + else + { + if ((VECTOR_push_back(moduleHandleData->personalities, &personality, 1)) != 0) + { + /*Codes_SRS_IOTHUBHTTP_02_016: [If adding the new triplet fails, then IoTHubClient_Create shall return.]*/ + LogError("VECTOR_push_back failed"); + PERSONALITY_destroy(personality); + free(personality); + result = NULL; + } + else + { + resultPtr = VECTOR_back(moduleHandleData->personalities); + result = *resultPtr; + } + } + } + else + { + result = *resultPtr; + } + return result; +} + +static IOTHUB_MESSAGE_HANDLE IoTHubMessage_CreateFromGWMessage(MESSAGE_HANDLE message) +{ + IOTHUB_MESSAGE_HANDLE result; + const CONSTBUFFER* content = Message_GetContent(message); + /*Codes_SRS_IOTHUBHTTP_02_019: [If creating the IOTHUB_MESSAGE_HANDLE fails, then IoTHubHttp_Receive shall return.]*/ + result = IoTHubMessage_CreateFromByteArray(content->buffer, content->size); + if (result == NULL) + { + LogError("IoTHubMessage_CreateFromByteArray failed"); + /*return as is*/ + } + else + { + MAP_HANDLE iothubMessageProperties = IoTHubMessage_Properties(result); + CONSTMAP_HANDLE gwMessageProperties = Message_GetProperties(message); + const char* const* keys; + const char* const* values; + size_t nProperties; + if (ConstMap_GetInternals(gwMessageProperties, &keys, &values, &nProperties) != CONSTMAP_OK) + { + /*Codes_SRS_IOTHUBHTTP_02_019: [If creating the IOTHUB_MESSAGE_HANDLE fails, then IoTHubHttp_Receive shall return.]*/ + LogError("unable to get properties of the GW message"); + IoTHubMessage_Destroy(result); + result = NULL; + } + else + { + size_t i; + for (i = 0; i < nProperties; i++) + { + /*add all the properties of the GW message to the IOTHUB message*/ /*with the exception*/ + /*Codes_SRS_IOTHUBHTTP_02_018: [IoTHubHttp_Receive shall create a new IOTHUB_MESSAGE_HANDLE having the same content as the messageHandle and same properties with the exception of deviceName and deviceKey properties.]*/ + if ( + (strcmp(keys[i], "deviceName") != 0) && + (strcmp(keys[i], "deviceKey") != 0) + ) + { + + if (Map_AddOrUpdate(iothubMessageProperties, keys[i], values[i]) != MAP_OK) + { + /*Codes_SRS_IOTHUBHTTP_02_019: [If creating the IOTHUB_MESSAGE_HANDLE fails, then IoTHubHttp_Receive shall return.]*/ + LogError("unable to Map_AddOrUpdate"); + break; + } + } + } + + if (i == nProperties) + { + /*all is fine, return as is*/ + } + else + { + /*Codes_SRS_IOTHUBHTTP_02_019: [If creating the IOTHUB_MESSAGE_HANDLE fails, then IoTHubHttp_Receive shall return.]*/ + IoTHubMessage_Destroy(result); + result = NULL; + } + } + ConstMap_Destroy(gwMessageProperties); + } + return result; +} + +static void IoTHubHttp_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + /*Codes_SRS_IOTHUBHTTP_02_009: [If moduleHandle or messageHandle is NULL then IoTHubHttp_Receive shall do nothing.]*/ + if ( + (moduleHandle == NULL) || + (messageHandle == NULL) + ) + { + LogError("invalid arg moduleHandle=%p, messageHandle=%p", moduleHandle, messageHandle); + /*do nothing*/ + } + else + { + CONSTMAP_HANDLE properties = Message_GetProperties(messageHandle); + const char* source = ConstMap_GetValue(properties, SOURCE); /*properties is !=NULL by contract of Message*/ + + /*Codes_SRS_IOTHUBHTTP_02_010: [If message properties do not contain a property called "source" having the value set to "mapping" then IoTHubHttp_Receive shall do nothing.]*/ + if ( + (source == NULL) || + (strcmp(source, MAPPING)!=0) + ) + { + /*do nothing, the properties do not contain either "source" or "source":"mapping"*/ + } + else + { + /*Codes_SRS_IOTHUBHTTP_02_011: [If message properties do not contain a property called "deviceName" having a non-NULL value then IoTHubHttp_Receive shall do nothing.]*/ + const char* deviceName = ConstMap_GetValue(properties, DEVICENAME); + if (deviceName == NULL) + { + /*do nothing, not a message for this module*/ + } + else + { + /*Codes_SRS_IOTHUBHTTP_02_012: [If message properties do not contain a property called "deviceKey" having a non-NULL value then IoTHubHttp_Receive shall do nothing.]*/ + const char* deviceKey = ConstMap_GetValue(properties, DEVICEKEY); + if (deviceKey == NULL) + { + /*do nothing, missing device key*/ + } + else + { + IOTHUBHTTP_HANDLE_DATA* moduleHandleData = moduleHandle; + /*Codes_SRS_IOTHUBHTTP_02_013: [If the deviceName does not exist in the PERSONALITY collection then IoTHubHttp_Receive shall create a new IOTHUB_CLIENT_HANDLE by a call to IoTHubClient_CreateWithTransport.]*/ + + PERSONALITY* whereIsIt = PERSONALITY_find_or_create(moduleHandleData, deviceName, deviceKey); + if (whereIsIt == NULL) + { + /*Codes_SRS_IOTHUBHTTP_02_014: [If creating the PERSONALITY fails then IoTHubHttp_Receive shall return.]*/ + /*do nothing, device was not added to the GW*/ + LogError("unable to PERSONALITY_find_or_create"); + } + else + { + IOTHUB_MESSAGE_HANDLE iotHubMessage = IoTHubMessage_CreateFromGWMessage(messageHandle); + if(iotHubMessage == NULL) + { + LogError("unable to IoTHubMessage_CreateFromGWMessage (internal)"); + } + else + { + /*Codes_SRS_IOTHUBHTTP_02_020: [IoTHubHttp_Receive shall call IoTHubClient_SendEventAsync passing the IOTHUB_MESSAGE_HANDLE.*/ + if (IoTHubClient_SendEventAsync(whereIsIt->iothubHandle, iotHubMessage, NULL, NULL) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBHTTP_02_021: [If IoTHubClient_SendEventAsync fails then IoTHubHttp_Receive shall return.]*/ + LogError("unable to IoTHubClient_SendEventAsync"); + } + else + { + /*all is fine, message has been accepted for delivery*/ + } + IoTHubMessage_Destroy(iotHubMessage); + } + } + } + } + } + ConstMap_Destroy(properties); + } + /*Codes_SRS_IOTHUBHTTP_02_022: [IoTHubHttp_Receive shall return.]*/ +} + +static const MODULE_APIS Module_GetAPIS_Impl = +{ + /*Codes_SRS_IOTHUBHTTP_02_026: [The MODULE_APIS structure shall have non-NULL Module_Create, Module_Destroy, and Module_Receive fields.]*/ + IoTHubHttp_Create, + IoTHubHttp_Destroy, + IoTHubHttp_Receive +}; + +/*Codes_SRS_IOTHUBHTTP_02_025: [Module_GetAPIS shall return a non-NULL pointer.]*/ +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE)(void) +#else +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + return &Module_GetAPIS_Impl; +} \ No newline at end of file diff --git a/modules/iothubhttp/src/iothubhttp_hl.c b/modules/iothubhttp/src/iothubhttp_hl.c new file mode 100644 index 00000000..045f0dbc --- /dev/null +++ b/modules/iothubhttp/src/iothubhttp_hl.c @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include "azure_c_shared_utility/gballoc.h" + +#include "iothubhttp_hl.h" +#include "iothubhttp.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "parson.h" + +#define SUFFIX "IoTHubSuffix" +#define HUBNAME "IoTHubName" + +static MODULE_HANDLE IoTHubHttp_HL_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + MODULE_HANDLE *result; + if ((busHandle == NULL) || (configuration == NULL)) + { + /*Codes_SRS_IOTHUBHTTP_HL_17_001: [If busHandle is NULL then IoTHubHttp_HL_Create shall fail and return NULL.]*/ + /*Codes_SRS_IOTHUBHTTP_HL_17_002: [If configuration is NULL then IoTHubHttp_HL_Create shall fail and return NULL.]*/ + LogError("Invalid NULL parameter, busHandle=[%p] configuration=[%p]", busHandle, configuration); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBHTTP_HL_17_004: [ IoTHubHttp_HL_Create shall parse the configuration as a JSON string. ]*/ + JSON_Value* json = json_parse_string((const char*)configuration); + if (json == NULL) + { + /*Codes_SRS_IOTHUBHTTP_HL_17_003: [ If configuration is not a JSON string, then IoTHubHttp_HL_Create shall fail and return NULL. ]*/ + LogError("Unable to parse json string"); + result = NULL; + } + else + { + JSON_Object* obj = json_value_get_object(json); + if (obj == NULL) + { + /*Codes_SRS_IOTHUBHTTP_HL_17_005: [ If parsing configuration fails, IoTHubHttp_HL_Create shall fail and return NULL. ]*/ + LogError("Expected a JSON Object in configuration"); + result = NULL; + } + else + { + const char * IoTHubName; + const char * IoTHubSuffix; + if ((IoTHubName = json_object_get_string(obj, HUBNAME)) == NULL) + { + /*Codes_SRS_IOTHUBHTTP_HL_17_006: [ If the JSON object does not contain a value named "IoTHubName" then IoTHubHttp_HL_Create shall fail and return NULL. ]*/ + LogError("Did not find expected %s configuration", HUBNAME); + result = NULL; + } + else if ((IoTHubSuffix = json_object_get_string(obj, SUFFIX)) == NULL) + { + /*Codes_SRS_IOTHUBHTTP_HL_17_007: [ If the JSON object does not contain a value named "IoTHubSuffix" then IoTHubHttp_HL_Create shall fail and return NULL. ]*/ + LogError("Did not find expected %s configuration", SUFFIX); + result = NULL; + } + else + { + /*Codes_SRS_IOTHUBHTTP_HL_17_008: [ IoTHubHttp_HL_Create shall invoke iothubhttp Module's create, using the busHandle, IotHubName, and IoTHubSuffix. ]*/ + IOTHUBHTTP_CONFIG llConfiguration; + llConfiguration.IoTHubName = IoTHubName; + llConfiguration.IoTHubSuffix = IoTHubSuffix; + /*Codes_SRS_IOTHUBHTTP_HL_17_009: [ When the lower layer IoTHubHttp create succeeds, IoTHubHttp_HL_Create shall succeed and return a non-NULL value. ]*/ + /*Codes_SRS_IOTHUBHTTP_HL_17_010: [ If the lower layer IoTHubHttp create fails, IoTHubHttp_HL_Create shall fail and return NULL. ]*/ + result = MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE)()->Module_Create(busHandle, &llConfiguration); + } + } + json_value_free(json); + } + } + return result; +} + +static void IoTHubHttp_HL_Destroy(MODULE_HANDLE moduleHandle) +{ + /*Codes_SRS_IOTHUBHTTP_HL_17_012: [ IoTHubHttp_HL_Destroy shall free all used resources. ]*/ + MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE)()->Module_Destroy(moduleHandle); +} + + +static void IoTHubHttp_HL_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + /*Codes_SRS_IOTHUBHTTP_HL_17_011: [ IoTHubHttp_HL_Receive shall pass the received parameters to the underlying IoTHubHttp receive function. ]*/ + MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE)()->Module_Receive(moduleHandle, messageHandle); +} + +/*Codes_SRS_IOTHUBHTTP_HL_17_014: [ The MODULE_APIS structure shall have non-NULL Module_Create, Module_Destroy, and Module_Receive fields. ]*/ +static const MODULE_APIS IoTHubHttp_HL_GetAPIS_Impl = +{ + IoTHubHttp_HL_Create, + IoTHubHttp_HL_Destroy, + IoTHubHttp_HL_Receive +}; + + +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE_HL)(void) +#else +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + /*Codes_SRS_IOTHUBHTTP_HL_17_013: [ Module_GetAPIS shall return a non-NULL pointer. ]*/ + return &IoTHubHttp_HL_GetAPIS_Impl; +} \ No newline at end of file diff --git a/modules/iothubhttp/tests/CMakeLists.txt b/modules/iothubhttp/tests/CMakeLists.txt new file mode 100644 index 00000000..e1192433 --- /dev/null +++ b/modules/iothubhttp/tests/CMakeLists.txt @@ -0,0 +1,8 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) +#this is CMakeLists for iothubhttp module tests + +add_subdirectory(iothubhttp_unittests) +add_subdirectory(iothubhttp_hl_unittests) diff --git a/modules/iothubhttp/tests/iothubhttp_hl_unittests/CMakeLists.txt b/modules/iothubhttp/tests/iothubhttp_hl_unittests/CMakeLists.txt new file mode 100644 index 00000000..3b744c45 --- /dev/null +++ b/modules/iothubhttp/tests/iothubhttp_hl_unittests/CMakeLists.txt @@ -0,0 +1,22 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for the iothubhttp_hl_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName iothubhttp_hl_unittests) +set(${theseTestsName}_cpp_files +${theseTestsName}.cpp +) + +set(${theseTestsName}_c_files + ../../src/iothubhttp_hl.c +) + +set(${theseTestsName}_h_files +) + +include_directories(${GW_INC} ../../inc) + +build_test_artifacts(${theseTestsName} ON) \ No newline at end of file diff --git a/modules/iothubhttp/tests/iothubhttp_hl_unittests/iothubhttp_hl_unittests.cpp b/modules/iothubhttp/tests/iothubhttp_hl_unittests/iothubhttp_hl_unittests.cpp new file mode 100644 index 00000000..17145efd --- /dev/null +++ b/modules/iothubhttp/tests/iothubhttp_hl_unittests/iothubhttp_hl_unittests.cpp @@ -0,0 +1,462 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" +#include "azure_c_shared_utility/lock.h" + +#include "iothubhttp.h" +#include "iothubhttp_hl.h" +#include "parson.h" + + +#define GBALLOC_H +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit + +}; + + +/*forward declarations*/ +static MODULE_HANDLE IoTHubHttp_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +/*this destroys (frees resources) of the module parameter*/ +static void IoTHubHttp_Destroy(MODULE_HANDLE moduleHandle); +/*this is the module's callback function - gets called when a message is to be received by the module*/ +static void IoTHubHttp_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); + +static const MODULE_APIS IoTHubHttp_GetAPIS_Impl = +{ + IoTHubHttp_Create, + IoTHubHttp_Destroy, + IoTHubHttp_Receive +}; + +typedef struct json_value_t +{ + int fake; +} JSON_Value; + +typedef struct json_object_t +{ + int fake; +} JSON_Object; + +/*these are simple cached variables*/ +static pfModule_Create Module_Create = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Destroy Module_Destroy = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Receive Module_Receive = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ + + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +TYPED_MOCK_CLASS(CIoTHubHTTPHLMocks, CGlobalMock) +{ +public: + + /* IoTHubHttp LL mocks*/ + MOCK_STATIC_METHOD_0(, const MODULE_APIS*, MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE)) + MOCK_METHOD_END(const MODULE_APIS*, (const MODULE_APIS*)&IoTHubHttp_GetAPIS_Impl); + + MOCK_STATIC_METHOD_2(, MODULE_HANDLE, IoTHubHttp_Create, MESSAGE_BUS_HANDLE, busHandle, const void*, configuration) + MOCK_METHOD_END(MODULE_HANDLE, malloc(1)); + + MOCK_STATIC_METHOD_1(, void, IoTHubHttp_Destroy, MODULE_HANDLE, moduleHandle) + free(moduleHandle); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_2(, void, IoTHubHttp_Receive, MODULE_HANDLE, moduleHandle, MESSAGE_HANDLE, messageHandle) + MOCK_VOID_METHOD_END(); + + + + /* Parson Mocks */ + MOCK_STATIC_METHOD_1(, JSON_Value*, json_parse_string, const char *, parseString) + JSON_Value* value = NULL; + if (parseString != NULL) + { + value = (JSON_Value*)malloc(1); + } + MOCK_METHOD_END(JSON_Value*, value); + + MOCK_STATIC_METHOD_1(, JSON_Object*, json_value_get_object, const JSON_Value*, value) + JSON_Object* object = NULL; + if (value != NULL) + { + object = (JSON_Object*)0x42; + } + MOCK_METHOD_END(JSON_Object*, object); + + MOCK_STATIC_METHOD_2(, const char*, json_object_get_string, const JSON_Object*, object, const char*, name) + const char * result2; + if (strcmp(name, "IoTHubSuffix") == 0) + { + result2 = "suffix.name"; + } + else if (strcmp(name, "IoTHubName") == 0) + { + result2 = "aHubName"; + } + else + { + result2 = NULL; + } + MOCK_METHOD_END(const char*, result2); + + MOCK_STATIC_METHOD_1(, void, json_value_free, JSON_Value*, value) + free(value); + MOCK_VOID_METHOD_END(); + + + /* malloc */ + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() +}; + +DECLARE_GLOBAL_MOCK_METHOD_0(CIoTHubHTTPHLMocks, , const MODULE_APIS*, MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE)); + +DECLARE_GLOBAL_MOCK_METHOD_2(CIoTHubHTTPHLMocks, , MODULE_HANDLE, IoTHubHttp_Create, MESSAGE_BUS_HANDLE, busHandle, const void*, configuration); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPHLMocks, , void, IoTHubHttp_Destroy, MODULE_HANDLE, moduleHandle); +DECLARE_GLOBAL_MOCK_METHOD_2(CIoTHubHTTPHLMocks, , void, IoTHubHttp_Receive, MODULE_HANDLE, moduleHandle, MESSAGE_HANDLE, messageHandle); + +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPHLMocks, , JSON_Value*, json_parse_string, const char *, filename); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPHLMocks, , JSON_Object*, json_value_get_object, const JSON_Value*, value); +DECLARE_GLOBAL_MOCK_METHOD_2(CIoTHubHTTPHLMocks, , const char*, json_object_get_string, const JSON_Object*, object, const char*, name); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPHLMocks, , void, json_value_free, JSON_Value*, value); + +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPHLMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPHLMocks, , void, gballoc_free, void*, ptr); + + +BEGIN_TEST_SUITE(iothubhttp_hl_unittests) + +TEST_SUITE_INITIALIZE(TestClassInitialize) +{ + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + + Module_Create = Module_GetAPIS()->Module_Create; + Module_Destroy = Module_GetAPIS()->Module_Destroy; + Module_Receive = Module_GetAPIS()->Module_Receive; +} + +TEST_SUITE_CLEANUP(TestClassCleanup) +{ + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); +} + +TEST_FUNCTION_INITIALIZE(TestMethodInitialize) +{ + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } +} + +TEST_FUNCTION_CLEANUP(TestMethodCleanup) +{ + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } +} + + +//Tests_SRS_IOTHUBHTTP_HL_17_013: [ Module_GetAPIS shall return a non-NULL pointer. ] +TEST_FUNCTION(Module_GetAPIS_returns_non_NULL) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + + ///act + auto MODULEAPIS = Module_GetAPIS(); + + ///assert + ASSERT_IS_NOT_NULL(MODULEAPIS); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_IOTHUBHTTP_HL_17_014: [ The MODULE_APIS structure shall have non-NULL Module_Create, Module_Destroy, and Module_Receive fields. ] +TEST_FUNCTION(Module_GetAPIS_returns_non_NULL_fields) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + + ///act + auto MODULEAPIS = Module_GetAPIS(); + + ///assert + ASSERT_IS_NOT_NULL(MODULEAPIS->Module_Create); + ASSERT_IS_NOT_NULL(MODULEAPIS->Module_Destroy); + ASSERT_IS_NOT_NULL(MODULEAPIS->Module_Receive); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup +} + +//Tests_SRS_IOTHUBHTTP_HL_17_004: [ IoTHubHttp_HL_Create shall parse the configuration as a JSON string. ] +//Tests_SRS_IOTHUBHTTP_HL_17_008: [IoTHubHttp_HL_Create shall invoke IoTHubHttp Module's create, using the busHandle, IotHubName, and IoTHubSuffix. ] +//Tests_SRS_IOTHUBHTTP_HL_17_009: [ When the lower layer IoTHubHttp create succeeds, IoTHubHttp_HL_Create shall succeed and return a non-NULL value. +TEST_FUNCTION(IoTHubHttp_HL_Create_success) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + const char * validJsonString = "calling it valid makes it so"; + MESSAGE_BUS_HANDLE busHandle = (MESSAGE_BUS_HANDLE)0x42; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(validJsonString)); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "IoTHubName")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "IoTHubSuffix")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE)()); + STRICT_EXPECTED_CALL(mocks, IoTHubHttp_Create(busHandle, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto result = Module_Create(busHandle, validJsonString); + ///assert + + ASSERT_IS_NOT_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(result); +} + +//Tests_SRS_IOTHUBHTTP_HL_17_010: [ If the lower layer IoTHubHttp create fails, IoTHubHttp_HL_Create shall fail and return NULL. ] +TEST_FUNCTION(IoTHubHttp_HL_Create_ll_module_null_returns_null) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + const char * validJsonString = "calling it valid makes it so"; + MESSAGE_BUS_HANDLE busHandle = (MESSAGE_BUS_HANDLE)0x42; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(validJsonString)); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "IoTHubName")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "IoTHubSuffix")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE)()); + STRICT_EXPECTED_CALL(mocks, IoTHubHttp_Create(busHandle, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .SetFailReturn((MODULE_HANDLE)NULL); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto result = Module_Create(busHandle, validJsonString); + ///assert + + ASSERT_IS_NULL(result); + + ///cleanup +} + +//Tests_SRS_IOTHUBHTTP_HL_17_007: [ If the JSON object does not contain a value named "IoTHubSuffix" then IoTHubHttp_HL_Create shall fail and return NULL. ] +TEST_FUNCTION(IoTHubHttp_HL_Create_IoTHubSuffix_not_found_returns_null) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + const char * validJsonString = "calling it valid makes it so"; + MESSAGE_BUS_HANDLE busHandle = (MESSAGE_BUS_HANDLE)0x42; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(validJsonString)); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "IoTHubName")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "IoTHubSuffix")) + .IgnoreArgument(1) + .SetFailReturn((const char *)NULL); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto result = Module_Create(busHandle, validJsonString); + ///assert + + ASSERT_IS_NULL(result); + + ///cleanup +} + +//Tests_SRS_IOTHUBHTTP_HL_17_006: [ If the JSON object does not contain a value named "IoTHubName" then IoTHubHttp_HL_Create shall fail and return NULL. ] +TEST_FUNCTION(IoTHubHttp_HL_Create_IoTHubName_not_found_returns_null) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + const char * validJsonString = "calling it valid makes it so"; + MESSAGE_BUS_HANDLE busHandle = (MESSAGE_BUS_HANDLE)0x42; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(validJsonString)); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "IoTHubName")) + .IgnoreArgument(1) + .SetFailReturn((const char *)NULL); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto result = Module_Create(busHandle, validJsonString); + ///assert + + ASSERT_IS_NULL(result); + + ///cleanup +} + +//Tests_SRS_IOTHUBHTTP_HL_17_005: [ If parsing configuration fails, IoTHubHttp_HL_Create shall fail and return NULL. ] +TEST_FUNCTION(IoTHubHttp_HL_Create_get_object_null_returns_null) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + const char * validJsonString = "calling it valid makes it so"; + MESSAGE_BUS_HANDLE busHandle = (MESSAGE_BUS_HANDLE)0x42; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(validJsonString)); + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((JSON_Object*)NULL); + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + auto result = Module_Create(busHandle, validJsonString); + ///assert + + ASSERT_IS_NULL(result); +} + +//Tests_SRS_IOTHUBHTTP_HL_17_005: [ If parsing configuration fails, IoTHubHttp_HL_Create shall fail and return NULL. ] +//Tests_SRS_IOTHUBHTTP_HL_17_003: [ If configuration is not a JSON string, then IoTHubHttp_HL_Create shall fail and return NULL. ] +TEST_FUNCTION(IoTHubHttp_HL_Create_parse_string_null_returns_null) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + const char * invalidJsonString = "calling it invalid has the same effect"; + MESSAGE_BUS_HANDLE busHandle = (MESSAGE_BUS_HANDLE)0x42; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(invalidJsonString)) + .SetFailReturn((JSON_Value*)NULL); + + ///act + auto result = Module_Create(busHandle, invalidJsonString); + ///assert + + ASSERT_IS_NULL(result); +} + +//Tests_SRS_IOTHUBHTTP_HL_17_001: [If busHandle is NULL then IoTHubHttp_HL_Create shall fail and return NULL.] +TEST_FUNCTION(IoTHubHttp_HL_Create_bus_handle_null_returns_null) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + const char * validJsonString = "calling it valid makes it so"; + MESSAGE_BUS_HANDLE busHandle = NULL; + + ///act + auto result = Module_Create(busHandle, validJsonString); + ///assert + + ASSERT_IS_NULL(result); +} + +//Tests_SRS_IOTHUBHTTP_HL_17_002: [If configuration is NULL then IoTHubHttp_HL_Create shall fail and return NULL.] +TEST_FUNCTION(IoTHubHttp_HL_Create_config_null_returns_null) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + const char * nullString = NULL; + MESSAGE_BUS_HANDLE busHandle = (MESSAGE_BUS_HANDLE)0x42; + + ///act + auto result = Module_Create(busHandle, nullString); + ///assert + + ASSERT_IS_NULL(result); +} + +//Tests_SRS_IOTHUBHTTP_HL_17_012: [ IoTHubHttp_HL_Destroy shall free all used resources. ] +TEST_FUNCTION(IoTHubHttp_HL_Destroy_does_everything) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + const char * validJsonString = "calling it valid makes it so"; + MESSAGE_BUS_HANDLE busHandle = (MESSAGE_BUS_HANDLE)0x42; + auto result = Module_Create(busHandle, validJsonString); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE)()); + STRICT_EXPECTED_CALL(mocks, IoTHubHttp_Destroy(result)); + + ///act + Module_Destroy(result); + + ///assert + ///cleanup +} + +//Tests_SRS_IOTHUBHTTP_HL_17_011: [ IoTHubHttp_HL_Receive shall pass the received parameters to the underlying IoTHubHttp receive function. ] +TEST_FUNCTION(IoTHubHttp_HL_Receive_does_everything) +{ + ///arrange + CIoTHubHTTPHLMocks mocks; + const char * validJsonString = "calling it valid makes it so"; + MESSAGE_BUS_HANDLE busHandle = (MESSAGE_BUS_HANDLE)0x42; + MESSAGE_HANDLE messageHandle = (MESSAGE_HANDLE)0x42; + auto result = Module_Create(busHandle, validJsonString); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(IOTHUBHTTP_MODULE)()); + STRICT_EXPECTED_CALL(mocks, IoTHubHttp_Receive(result, messageHandle)); + + ///act + Module_Receive(result, messageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(result); +} + +END_TEST_SUITE(iothubhttp_hl_unittests) diff --git a/modules/iothubhttp/tests/iothubhttp_hl_unittests/main.c b/modules/iothubhttp/tests/iothubhttp_hl_unittests/main.c new file mode 100644 index 00000000..7623bafb --- /dev/null +++ b/modules/iothubhttp/tests/iothubhttp_hl_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(iothubhttp_hl_unittests, failedTestCount); + return failedTestCount; +} diff --git a/modules/iothubhttp/tests/iothubhttp_unittests/CMakeLists.txt b/modules/iothubhttp/tests/iothubhttp_unittests/CMakeLists.txt new file mode 100644 index 00000000..a41df717 --- /dev/null +++ b/modules/iothubhttp/tests/iothubhttp_unittests/CMakeLists.txt @@ -0,0 +1,22 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for iothubhttp_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName iothubhttp_unittests) +set(${theseTestsName}_cpp_files +${theseTestsName}.cpp +) + +set(${theseTestsName}_c_files + ../../src/iothubhttp.c +) + +set(${theseTestsName}_h_files +) + +include_directories(${GW_INC} ../../inc) + +build_test_artifacts(${theseTestsName} ON) \ No newline at end of file diff --git a/modules/iothubhttp/tests/iothubhttp_unittests/iothubhttp_unittests.cpp b/modules/iothubhttp/tests/iothubhttp_unittests/iothubhttp_unittests.cpp new file mode 100644 index 00000000..1b1cf437 --- /dev/null +++ b/modules/iothubhttp/tests/iothubhttp_unittests/iothubhttp_unittests.cpp @@ -0,0 +1,2902 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +#include "module.h" +#include "azure_c_shared_utility/lock.h" +#include "azure_c_shared_utility/vector.h" +#include "azure_c_shared_utility/strings.h" +#include "iothubtransport.h" +#include "iothub_message.h" +#include "message_bus.h" +#include "message.h" +#include "messageproperties.h" + +#define GBALLOC_H +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit + +#include "vector.c" +#include "strings.c" +}; + +#include "iothubhttp.h" +#include "iothub_client.h" +#include "message.h" +#include "azure_c_shared_utility/constmap.h" +#include "azure_c_shared_utility/map.h" + +DEFINE_MICROMOCK_ENUM_TO_STRING(IOTHUBMESSAGE_DISPOSITION_RESULT, IOTHUBMESSAGE_DISPOSITION_RESULT_VALUES); + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + +static size_t currentmalloc_call; +static size_t whenShallmalloc_fail; + +static size_t currentConstMap_Create_call; +static size_t whenShallConstMap_Create_fail; + +static size_t currentCONSTBUFFER_Create_call; +static size_t whenShallCONSTBUFFER_Create_fail; +static size_t currentCONSTBUFFER_refCount; + +static size_t currentVECTOR_create_call; +static size_t whenShallVECTOR_create_fail; + +static size_t currentVECTOR_push_back_call; +static size_t whenShallVECTOR_push_back_fail; + +/*different STRING constructors*/ +static size_t currentSTRING_new_call; +static size_t whenShallSTRING_new_fail; + +static size_t currentSTRING_clone_call; +static size_t whenShallSTRING_clone_fail; + +static size_t currentSTRING_construct_call; +static size_t whenShallSTRING_construct_fail; + +static size_t currentSTRING_concat_call; +static size_t whenShallSTRING_concat_fail; + +static size_t currentSTRING_empty_call; +static size_t whenShallSTRING_empty_fail; + +static size_t currentSTRING_concat_with_STRING_call; +static size_t whenShallSTRING_concat_with_STRING_fail; + +static size_t currentIoTHubMessage_CreateFromByteArray_call; +static size_t whenShallIoTHubMessage_CreateFromByteArray_fail; + +static size_t currentIoTHubClient_Create_call; +static size_t whenShallIoTHubClient_Create_fail; + +static IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC IoTHubHttp_receive_message_callback_function; +static void * IoTHubHttp_receive_message_userContext; +static const char * IoTHubHttp_receive_message_content; +static size_t IoTHubHttp_receive_message_size; + + +/*variable mock :(*/ +extern "C" const void* (*const HTTP_Protocol)(void) = (const void* (*)(void))((void*)11); + +#define MESSAGE_BUS_HANDLE_VALID ((MESSAGE_BUS_HANDLE)(1)) + +#define IOTHUB_CLIENT_HANDLE_VALID ((IOTHUB_CLIENT_HANDLE)(2)) +#define IOTHUB_MESSAGE_HANDLE_VALID ((IOTHUB_MESSAGE_HANDLE)7) + +#define MESSAGE_HANDLE_VALID ((MESSAGE_HANDLE)(3)) + +#define MESSAGE_HANDLE_WITHOUT_SOURCE ((MESSAGE_HANDLE)(4)) +#define CONSTMAP_HANDLE_WITHOUT_SOURCE ((CONSTMAP_HANDLE)(4)) + + +#define MESSAGE_HANDLE_WITH_SOURCE_NOT_SET_TO_MAPPING ((MESSAGE_HANDLE)(5)) +#define CONSTMAP_HANDLE_WITH_SOURCE_NOT_SET_TO_MAPPING ((CONSTMAP_HANDLE)(5)) + +#define MESSAGE_HANDLE_VALID_1 ((MESSAGE_HANDLE)(6)) +#define CONSTMAP_HANDLE_VALID_1 ((CONSTMAP_HANDLE)(6)) +static const CONSTBUFFER CONSTBUFFER_VALID_CONTENT1 = +{ + (unsigned char*)"5", + 1 +}; +static const CONSTBUFFER* CONSTBUFFER_VALID_1 = &CONSTBUFFER_VALID_CONTENT1; +static IOTHUB_CLIENT_HANDLE IOTHUB_CLIENT_HANDLE_VALID_1 = ((IOTHUB_CLIENT_HANDLE)(6)); +static IOTHUB_MESSAGE_HANDLE IOTHUB_MESSAGE_HANDLE_VALID_1 = ((IOTHUB_MESSAGE_HANDLE)(6)); +static MAP_HANDLE MAP_HANDLE_VALID_1 = ((MAP_HANDLE)(6)); +static const char* CONSTMAP_KEYS_VALID_1[4] = {"source", "deviceName", "deviceKey", "somethingExtra"}; +static const char* CONSTMAP_VALUES_VALID_1[4] = { "mapping", "firstDevice", "cheiaDeLaPoartaVerde", "blue" }; + +#define MESSAGE_HANDLE_VALID_2 ((MESSAGE_HANDLE)(7)) +#define CONSTMAP_HANDLE_VALID_2 ((CONSTMAP_HANDLE)(7)) +static const CONSTBUFFER CONSTBUFFER_VALID_CONTENT2 = +{ + NULL, + 0 +}; +static const CONSTBUFFER* CONSTBUFFER_VALID_2 = &CONSTBUFFER_VALID_CONTENT2; +static IOTHUB_CLIENT_HANDLE IOTHUB_CLIENT_HANDLE_VALID_2 = ((IOTHUB_CLIENT_HANDLE)(7)); +static IOTHUB_MESSAGE_HANDLE IOTHUB_MESSAGE_HANDLE_VALID_2 = ((IOTHUB_MESSAGE_HANDLE)(7)); +static MAP_HANDLE MAP_HANDLE_VALID_2 = ((MAP_HANDLE)(7)); +static const char* CONSTMAP_KEYS_VALID_2[3] = { "source", "deviceName", "deviceKey"}; +static const char* CONSTMAP_VALUES_VALID_2[3] = { "mapping", "secondDevice", "red"}; + +/*these are simple cached variables*/ +static pfModule_Create Module_Create = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Destroy Module_Destroy = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Receive Module_Receive = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ + +static const IOTHUBHTTP_CONFIG config_with_NULL_IoTHubName = {NULL, "devices.azure.com"}; +static const IOTHUBHTTP_CONFIG config_with_NULL_IoTHubSuffix = { "theIoTHub42", NULL}; +static const IOTHUBHTTP_CONFIG config_valid = { "theIoTHub42", "theAwesomeSuffix.com"}; + + +TYPED_MOCK_CLASS(CIoTHubHTTPMocks, CGlobalMock) +{ +public: + + // memory + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2; + currentmalloc_call++; + if (whenShallmalloc_fail>0) + { + if (currentmalloc_call == whenShallmalloc_fail) + { + result2 = NULL; + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + } + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + } + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + // ConstMap mocks + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, ConstMap_Clone, CONSTMAP_HANDLE, handle) + MOCK_METHOD_END(CONSTMAP_HANDLE, handle) + + MOCK_STATIC_METHOD_1(, void, ConstMap_Destroy, CONSTMAP_HANDLE, map) + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_4(, CONSTMAP_RESULT, ConstMap_GetInternals, CONSTMAP_HANDLE, handle, const char*const**, keys, const char*const**, values, size_t*, count) + if (handle == CONSTMAP_HANDLE_VALID_1) + { + *keys=CONSTMAP_KEYS_VALID_1; + *values = CONSTMAP_VALUES_VALID_1; + *count = 4; + } + else if (handle == CONSTMAP_HANDLE_VALID_2) + { + *keys = CONSTMAP_KEYS_VALID_2; + *values = CONSTMAP_VALUES_VALID_2; + *count = 3; + } + else + { + *count = 0; + } + MOCK_METHOD_END(CONSTMAP_RESULT, CONSTMAP_OK) + + // CONSTBUFFER mocks. + MOCK_STATIC_METHOD_2(, CONSTBUFFER_HANDLE, CONSTBUFFER_Create, const unsigned char*, source, size_t, size) + CONSTBUFFER_HANDLE result1; + currentCONSTBUFFER_Create_call++; + if (whenShallCONSTBUFFER_Create_fail == currentCONSTBUFFER_Create_call) + { + result1 = NULL; + } + else + { + result1 = (CONSTBUFFER_HANDLE)malloc(sizeof(CONSTBUFFER)); + (*(CONSTBUFFER*)result1).size = size; + if (size == 0) + { + (*(CONSTBUFFER*)result1).buffer = NULL; + } + else + { + unsigned char* temp = (unsigned char*)malloc(size); + memcpy(temp, source, size); + (*(CONSTBUFFER*)result1).buffer = temp; + } + currentCONSTBUFFER_refCount = 1; + } + MOCK_METHOD_END(CONSTBUFFER_HANDLE, result1) + + MOCK_STATIC_METHOD_1(, CONSTBUFFER_HANDLE, CONSTBUFFER_Clone, CONSTBUFFER_HANDLE, constbufferHandle) + CONSTBUFFER_HANDLE result2 =constbufferHandle; + currentCONSTBUFFER_refCount++; + MOCK_METHOD_END(CONSTBUFFER_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, const CONSTBUFFER*, CONSTBUFFER_GetContent, CONSTBUFFER_HANDLE, constbufferHandle) + CONSTBUFFER* result3 = (CONSTBUFFER*)constbufferHandle; + MOCK_METHOD_END(const CONSTBUFFER*, result3) + + MOCK_STATIC_METHOD_1(, void, CONSTBUFFER_Destroy, CONSTBUFFER_HANDLE, constbufferHandle) + --currentCONSTBUFFER_refCount; + if (currentCONSTBUFFER_refCount == 0) + { + CONSTBUFFER * fakeBuffer = (CONSTBUFFER*)constbufferHandle; + if (fakeBuffer->buffer != NULL) + { + free((void*)fakeBuffer->buffer); + } + free(fakeBuffer); + } + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, VECTOR_HANDLE, VECTOR_create, size_t, elementSize) + VECTOR_HANDLE result2; + currentVECTOR_create_call++; + if (currentVECTOR_create_call == whenShallVECTOR_create_fail) + { + result2 = NULL; + } + else + { + result2 = BASEIMPLEMENTATION::VECTOR_create(elementSize); + } + MOCK_METHOD_END(VECTOR_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, VECTOR_destroy, VECTOR_HANDLE, handle) + BASEIMPLEMENTATION::VECTOR_destroy(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_3(, int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements) + int result2; + currentVECTOR_push_back_call++; + if (currentVECTOR_push_back_call == whenShallVECTOR_push_back_fail) + { + result2 = __LINE__; + } + else + { + result2 = BASEIMPLEMENTATION::VECTOR_push_back(handle, elements, numElements); + } + MOCK_METHOD_END(int, result2) + + MOCK_STATIC_METHOD_2(, void*, VECTOR_element, VECTOR_HANDLE, handle, size_t, index) + MOCK_METHOD_END(void*, BASEIMPLEMENTATION::VECTOR_element(handle, index)) + + MOCK_STATIC_METHOD_3(, void*, VECTOR_find_if, VECTOR_HANDLE, handle, PREDICATE_FUNCTION, pred, const void*, value) + MOCK_METHOD_END(void*, BASEIMPLEMENTATION::VECTOR_find_if(handle, pred, value)) + + MOCK_STATIC_METHOD_1(, size_t, VECTOR_size, VECTOR_HANDLE, handle) + MOCK_METHOD_END(size_t, BASEIMPLEMENTATION::VECTOR_size(handle)) + + MOCK_STATIC_METHOD_1(, void*, VECTOR_back, VECTOR_HANDLE, handle) + MOCK_METHOD_END(void*, BASEIMPLEMENTATION::VECTOR_back(handle)) + + MOCK_STATIC_METHOD_1(, void, STRING_delete, STRING_HANDLE, s) + BASEIMPLEMENTATION::STRING_delete(s); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_0(, STRING_HANDLE, STRING_new) + STRING_HANDLE result2; + currentSTRING_new_call++; + if (whenShallSTRING_new_fail > 0) + { + if (currentSTRING_new_call == whenShallSTRING_new_fail) + { + result2 = (STRING_HANDLE)NULL; + } + else + { + result2 = BASEIMPLEMENTATION::STRING_new(); + } + } + else + { + result2 = BASEIMPLEMENTATION::STRING_new(); + } + MOCK_METHOD_END(STRING_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, STRING_HANDLE, STRING_clone, STRING_HANDLE, handle) + STRING_HANDLE result2; + currentSTRING_clone_call++; + if (whenShallSTRING_clone_fail > 0) + { + if (currentSTRING_clone_call == whenShallSTRING_clone_fail) + { + result2 = (STRING_HANDLE)NULL; + } + else + { + result2 = BASEIMPLEMENTATION::STRING_clone(handle); + } + } + else + { + result2 = BASEIMPLEMENTATION::STRING_clone(handle); + } + MOCK_METHOD_END(STRING_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, STRING_HANDLE, STRING_construct, const char*, source) + STRING_HANDLE result2; + currentSTRING_construct_call++; + if (whenShallSTRING_construct_fail > 0) + { + if (currentSTRING_construct_call == whenShallSTRING_construct_fail) + { + result2 = (STRING_HANDLE)NULL; + } + else + { + result2 = BASEIMPLEMENTATION::STRING_construct(source); + } + } + else + { + result2 = BASEIMPLEMENTATION::STRING_construct(source); + } + MOCK_METHOD_END(STRING_HANDLE, result2) + + MOCK_STATIC_METHOD_2(, int, STRING_concat, STRING_HANDLE, s1, const char*, s2) + currentSTRING_concat_call++; + MOCK_METHOD_END(int, (((whenShallSTRING_concat_fail > 0) && (currentSTRING_concat_call == whenShallSTRING_concat_fail)) ? __LINE__ : BASEIMPLEMENTATION::STRING_concat(s1, s2))); + + MOCK_STATIC_METHOD_2(, int, STRING_concat_with_STRING, STRING_HANDLE, s1, STRING_HANDLE, s2) + currentSTRING_concat_with_STRING_call++; + MOCK_METHOD_END(int, (((currentSTRING_concat_with_STRING_call > 0) && (currentSTRING_concat_with_STRING_call == whenShallSTRING_concat_with_STRING_fail)) ? __LINE__ : BASEIMPLEMENTATION::STRING_concat_with_STRING(s1, s2))); + + MOCK_STATIC_METHOD_1(, int, STRING_empty, STRING_HANDLE, s) + currentSTRING_concat_call++; + MOCK_METHOD_END(int, BASEIMPLEMENTATION::STRING_empty(s)); + + MOCK_STATIC_METHOD_1(, const char*, STRING_c_str, STRING_HANDLE, s) + MOCK_METHOD_END(const char*, BASEIMPLEMENTATION::STRING_c_str(s)) + + + MOCK_STATIC_METHOD_2(, IOTHUB_CLIENT_HANDLE, IoTHubClient_CreateWithTransport, TRANSPORT_HANDLE, transport, const IOTHUB_CLIENT_CONFIG*, config) + IOTHUB_CLIENT_HANDLE result2; + currentIoTHubClient_Create_call++; + if (whenShallIoTHubClient_Create_fail == currentIoTHubClient_Create_call) + { + result2 = NULL; + } + else + { + result2 = (IOTHUB_CLIENT_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(1); + } + + MOCK_METHOD_END(IOTHUB_CLIENT_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, IoTHubClient_Destroy, IOTHUB_CLIENT_HANDLE, iotHubClientHandle) + BASEIMPLEMENTATION::gballoc_free(iotHubClientHandle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message) + CONSTMAP_HANDLE result2; + if (message == MESSAGE_HANDLE_WITHOUT_SOURCE) + { + result2 = CONSTMAP_HANDLE_WITHOUT_SOURCE; + } + else if (message == MESSAGE_HANDLE_WITH_SOURCE_NOT_SET_TO_MAPPING) + { + result2 = CONSTMAP_HANDLE_WITH_SOURCE_NOT_SET_TO_MAPPING; + } + else if (message == MESSAGE_HANDLE_VALID_1) + { + result2 = CONSTMAP_HANDLE_VALID_1; + } + else if (message == MESSAGE_HANDLE_VALID_2) + { + result2 = CONSTMAP_HANDLE_VALID_2; + } + else + { + result2 = NULL; + } + MOCK_METHOD_END(CONSTMAP_HANDLE, result2) + + MOCK_STATIC_METHOD_2(, const char*, ConstMap_GetValue, CONSTMAP_HANDLE, handle, const char*, key) + const char* result2; + if (handle == CONSTMAP_HANDLE_WITHOUT_SOURCE) + { + result2 = NULL; + } + else if (handle == CONSTMAP_HANDLE_WITH_SOURCE_NOT_SET_TO_MAPPING) + { + if (strcmp(key, "source") == 0) + { + result2 = "notMapping"; + } + else + { + result2 = NULL; + } + } + else if (handle == CONSTMAP_HANDLE_VALID_1) + { + size_t i; + result2 = NULL; + for (i = 0; i < sizeof(CONSTMAP_KEYS_VALID_1)/sizeof(CONSTMAP_KEYS_VALID_1[0]); i++) + { + if (strcmp(CONSTMAP_KEYS_VALID_1[i], key) == 0) + { + result2 = CONSTMAP_VALUES_VALID_1[i]; + break; + } + } + } + else if (handle == CONSTMAP_HANDLE_VALID_2) + { + size_t i; + result2 = NULL; + for (i = 0; i < sizeof(CONSTMAP_KEYS_VALID_2)/sizeof(CONSTMAP_KEYS_VALID_2[0]); i++) + { + if (strcmp(CONSTMAP_KEYS_VALID_2[i], key) == 0) + { + result2 = CONSTMAP_VALUES_VALID_2[i]; + break; + } + } + } + else + { + result2 = NULL; + } + MOCK_METHOD_END(const char*, result2) + + MOCK_STATIC_METHOD_3(, MAP_RESULT, Map_AddOrUpdate, MAP_HANDLE, handle, const char*, key, const char*, value) + MOCK_METHOD_END(MAP_RESULT, MAP_OK) + + MOCK_STATIC_METHOD_3(, MAP_RESULT, Map_Add, MAP_HANDLE, handle, const char*, key, const char*, value) + MOCK_METHOD_END(MAP_RESULT, MAP_OK) + + MOCK_STATIC_METHOD_4(, IOTHUB_CLIENT_RESULT, IoTHubClient_SendEventAsync, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback) + MOCK_METHOD_END(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK) + + MOCK_STATIC_METHOD_3(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetMessageCallback, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback) + IoTHubHttp_receive_message_callback_function = messageCallback; + IoTHubHttp_receive_message_userContext = userContextCallback; + MOCK_METHOD_END(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_OK) + + + //// GW Message + MOCK_STATIC_METHOD_1(, MESSAGE_HANDLE, Message_Create, const MESSAGE_CONFIG*, cfg) + MOCK_METHOD_END(MESSAGE_HANDLE, (MESSAGE_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(1)) + + MOCK_STATIC_METHOD_1(, void, Message_Destroy, MESSAGE_HANDLE, message) + BASEIMPLEMENTATION::gballoc_free(message); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, const CONSTBUFFER *, Message_GetContent, MESSAGE_HANDLE, message) + const CONSTBUFFER * result2; + if (message == MESSAGE_HANDLE_VALID_1) + { + result2 = CONSTBUFFER_VALID_1; + } + else if (message == MESSAGE_HANDLE_VALID_2) + { + result2 = CONSTBUFFER_VALID_2; + } + else + { + result2 = NULL; + } + MOCK_METHOD_END(const CONSTBUFFER *, result2) + + // IoTHubMessage + + MOCK_STATIC_METHOD_1(, void, IoTHubMessage_Destroy, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle) + BASEIMPLEMENTATION::gballoc_free(iotHubMessageHandle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, MAP_HANDLE, IoTHubMessage_Properties, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle) + MAP_HANDLE result2; + if (iotHubMessageHandle == IOTHUB_MESSAGE_HANDLE_VALID_1) + { + result2 = MAP_HANDLE_VALID_1; + } + else if (iotHubMessageHandle == IOTHUB_MESSAGE_HANDLE_VALID_2) + { + result2 = MAP_HANDLE_VALID_2; + } + else + { + result2 = NULL; + } + MOCK_METHOD_END(MAP_HANDLE, result2) + + MOCK_STATIC_METHOD_2(, IOTHUB_MESSAGE_HANDLE, IoTHubMessage_CreateFromByteArray, const unsigned char*, byteArray, size_t, size) + IOTHUB_MESSAGE_HANDLE result2; + currentIoTHubMessage_CreateFromByteArray_call ++; + + if (whenShallIoTHubMessage_CreateFromByteArray_fail == currentIoTHubMessage_CreateFromByteArray_call) + { + result2 = NULL; + } + else + { + result2 = BASEIMPLEMENTATION::gballoc_malloc(1); + } + + MOCK_METHOD_END(IOTHUB_MESSAGE_HANDLE, result2); + + MOCK_STATIC_METHOD_3(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_GetByteArray, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const unsigned char**, buffer, size_t*, size) + *buffer = (const unsigned char*)IoTHubHttp_receive_message_content; + *size = IoTHubHttp_receive_message_size; + MOCK_METHOD_END(IOTHUB_MESSAGE_RESULT, IOTHUB_MESSAGE_OK) + + MOCK_STATIC_METHOD_1(, const char*, IoTHubMessage_GetString, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle) + MOCK_METHOD_END(const char*, IoTHubHttp_receive_message_content) + + MOCK_STATIC_METHOD_1(, IOTHUBMESSAGE_CONTENT_TYPE, IoTHubMessage_GetContentType, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle) + MOCK_METHOD_END(IOTHUBMESSAGE_CONTENT_TYPE, IOTHUBMESSAGE_BYTEARRAY) + + + // transport mocks + + MOCK_STATIC_METHOD_3(,TRANSPORT_HANDLE, IoTHubTransport_Create, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol, const char*, iotHubName, const char*, iotHubSuffix) + TRANSPORT_HANDLE result2 = (TRANSPORT_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(1); + MOCK_METHOD_END(TRANSPORT_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, IoTHubTransport_Destroy, TRANSPORT_HANDLE, transportHlHandle) + BASEIMPLEMENTATION::gballoc_free(transportHlHandle); + MOCK_VOID_METHOD_END() + + + + // bus + MOCK_STATIC_METHOD_2(, MESSAGE_BUS_RESULT, MessageBus_Publish, MESSAGE_BUS_HANDLE, bus, MESSAGE_HANDLE, message) + MOCK_METHOD_END(MESSAGE_BUS_RESULT, MESSAGE_BUS_OK) + +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , void, gballoc_free, void*, ptr); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , CONSTMAP_HANDLE, ConstMap_Clone, CONSTMAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , void, ConstMap_Destroy, CONSTMAP_HANDLE, map); +DECLARE_GLOBAL_MOCK_METHOD_2(CIoTHubHTTPMocks, , CONSTBUFFER_HANDLE, CONSTBUFFER_Create, const unsigned char*, source, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , CONSTBUFFER_HANDLE, CONSTBUFFER_Clone, CONSTBUFFER_HANDLE, constbufferHandle); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , const CONSTBUFFER*, CONSTBUFFER_GetContent, CONSTBUFFER_HANDLE, constbufferHandle); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , void, CONSTBUFFER_Destroy, CONSTBUFFER_HANDLE, constbufferHandle); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , VECTOR_HANDLE, VECTOR_create, size_t, elementSize) +DECLARE_GLOBAL_MOCK_METHOD_3(CIoTHubHTTPMocks, , int, VECTOR_push_back, VECTOR_HANDLE, handle, const void*, elements, size_t, numElements) +DECLARE_GLOBAL_MOCK_METHOD_2(CIoTHubHTTPMocks, , void*, VECTOR_element, VECTOR_HANDLE, handle, size_t, index) +DECLARE_GLOBAL_MOCK_METHOD_3(CIoTHubHTTPMocks, , void*, VECTOR_find_if, VECTOR_HANDLE, handle, PREDICATE_FUNCTION, pred, const void*, value) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , size_t, VECTOR_size, VECTOR_HANDLE, handle) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , void, VECTOR_destroy, VECTOR_HANDLE, handle) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , void, STRING_delete, STRING_HANDLE, s); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , STRING_HANDLE, STRING_construct, const char*, source); +DECLARE_GLOBAL_MOCK_METHOD_2(CIoTHubHTTPMocks, , int, STRING_concat, STRING_HANDLE, s1, const char*, s2); +DECLARE_GLOBAL_MOCK_METHOD_2(CIoTHubHTTPMocks, , int, STRING_concat_with_STRING, STRING_HANDLE, s1, STRING_HANDLE, s2); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , int, STRING_empty, STRING_HANDLE, s1); +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , const char*, STRING_c_str, STRING_HANDLE, s); +DECLARE_GLOBAL_MOCK_METHOD_2(CIoTHubHTTPMocks, , IOTHUB_CLIENT_HANDLE, IoTHubClient_CreateWithTransport, TRANSPORT_HANDLE, transport, const IOTHUB_CLIENT_CONFIG*, config) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , void, IoTHubClient_Destroy, IOTHUB_CLIENT_HANDLE, iotHubClientHandle) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , MESSAGE_HANDLE, Message_Create, const MESSAGE_CONFIG*, cfg) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , void, Message_Destroy, MESSAGE_HANDLE, message) +DECLARE_GLOBAL_MOCK_METHOD_2(CIoTHubHTTPMocks, , const char*, ConstMap_GetValue, CONSTMAP_HANDLE, handle, const char*, key) +DECLARE_GLOBAL_MOCK_METHOD_3(CIoTHubHTTPMocks, , MAP_RESULT, Map_AddOrUpdate, MAP_HANDLE, handle, const char*, key, const char*, value); +DECLARE_GLOBAL_MOCK_METHOD_3(CIoTHubHTTPMocks, , MAP_RESULT, Map_Add, MAP_HANDLE, handle, const char*, key, const char*, value); +DECLARE_GLOBAL_MOCK_METHOD_4(CIoTHubHTTPMocks, , CONSTMAP_RESULT, ConstMap_GetInternals, CONSTMAP_HANDLE, handle, const char*const**, keys, const char*const**, values, size_t*, count) +DECLARE_GLOBAL_MOCK_METHOD_4(CIoTHubHTTPMocks, ,IOTHUB_CLIENT_RESULT, IoTHubClient_SendEventAsync, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback) +DECLARE_GLOBAL_MOCK_METHOD_3(CIoTHubHTTPMocks, , IOTHUB_CLIENT_RESULT, IoTHubClient_SetMessageCallback, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , const CONSTBUFFER *, Message_GetContent, MESSAGE_HANDLE, message) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , void, IoTHubMessage_Destroy, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle) +DECLARE_GLOBAL_MOCK_METHOD_2(CIoTHubHTTPMocks, , IOTHUB_MESSAGE_HANDLE, IoTHubMessage_CreateFromByteArray, const unsigned char*, byteArray, size_t, size) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , MAP_HANDLE, IoTHubMessage_Properties, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle) +DECLARE_GLOBAL_MOCK_METHOD_3(CIoTHubHTTPMocks, , IOTHUB_MESSAGE_RESULT, IoTHubMessage_GetByteArray, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const unsigned char**, buffer, size_t*, size) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , const char*, IoTHubMessage_GetString, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , IOTHUBMESSAGE_CONTENT_TYPE, IoTHubMessage_GetContentType, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , void*, VECTOR_back, VECTOR_HANDLE, handle) +DECLARE_GLOBAL_MOCK_METHOD_3(CIoTHubHTTPMocks, , TRANSPORT_HANDLE, IoTHubTransport_Create, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol, const char*, iotHubName, const char*, iotHubSuffix) +DECLARE_GLOBAL_MOCK_METHOD_1(CIoTHubHTTPMocks, , void, IoTHubTransport_Destroy, TRANSPORT_HANDLE, transportHlHandle) +DECLARE_GLOBAL_MOCK_METHOD_2(CIoTHubHTTPMocks, , MESSAGE_BUS_RESULT, MessageBus_Publish, MESSAGE_BUS_HANDLE, bus, MESSAGE_HANDLE, message) + +BEGIN_TEST_SUITE(iothubhttp_unittests) + + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + + Module_Create = Module_GetAPIS()->Module_Create; + Module_Destroy = Module_GetAPIS()->Module_Destroy; + Module_Receive = Module_GetAPIS()->Module_Receive; + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + + currentmalloc_call = 0; + whenShallmalloc_fail = 0; + + currentConstMap_Create_call = 0; + whenShallConstMap_Create_fail = 0; + + currentCONSTBUFFER_Create_call = 0; + whenShallCONSTBUFFER_Create_fail = 0; + currentCONSTBUFFER_refCount = 0; + + currentVECTOR_create_call = 0; + whenShallVECTOR_create_fail = 0; + + currentVECTOR_push_back_call = 0; + whenShallVECTOR_push_back_fail = 0; + + currentSTRING_new_call = 0; + whenShallSTRING_new_fail = 0; + + currentSTRING_clone_call = 0; + whenShallSTRING_clone_fail = 0; + + currentSTRING_construct_call = 0; + whenShallSTRING_construct_fail = 0; + + currentSTRING_concat_call = 0; + whenShallSTRING_concat_fail = 0; + + currentSTRING_empty_call = 0; + whenShallSTRING_empty_fail = 0; + + currentSTRING_concat_with_STRING_call = 0; + whenShallSTRING_concat_with_STRING_fail = 0; + + currentIoTHubMessage_CreateFromByteArray_call = 0; + whenShallIoTHubMessage_CreateFromByteArray_fail = 0; + + currentIoTHubClient_Create_call = 0; + whenShallIoTHubClient_Create_fail = 0; + + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + } + + /*Tests_SRS_IOTHUBHTTP_02_025: [Module_GetAPIS shall return a non-NULL pointer.]*/ + TEST_FUNCTION(Module_GetAPIS_returns_non_NULL) + { + ///arrange + CIoTHubHTTPMocks mocks; + + ///act + auto MODULEAPIS = Module_GetAPIS(); + + ///assert + ASSERT_IS_NOT_NULL(MODULEAPIS); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_IOTHUBHTTP_02_026: [The MODULE_APIS structure shall have non-NULL Module_Create, Module_Destroy, and Module_Receive fields.]*/ + TEST_FUNCTION(Module_GetAPIS_returns_non_NULL_fields) + { + ///arrange + CIoTHubHTTPMocks mocks; + + ///act + auto MODULEAPIS = Module_GetAPIS(); + + ///assert + ASSERT_IS_NOT_NULL(MODULEAPIS->Module_Create); + ASSERT_IS_NOT_NULL(MODULEAPIS->Module_Destroy); + ASSERT_IS_NOT_NULL(MODULEAPIS->Module_Receive); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_IOTHUBHTTP_02_001: [If busHandle is NULL then IoTHubHttp_Create shall fail and return NULL.]*/ + TEST_FUNCTION(IoTHubHttp_Create_with_NULL_MESSAGE_BUS_HANDLE_returns_NULL) + { + ///arrange + CIoTHubHTTPMocks mocks; + + ///act + auto result = Module_Create(NULL, &config_valid); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_IOTHUBHTTP_02_002: [If configuration is NULL then IoTHubHttp_Create shall fail and return NULL.]*/ + TEST_FUNCTION(IoTHubHttp_Create_with_NULL_config_returns_NULL) + { + ///arrange + CIoTHubHTTPMocks mocks; + + ///act + auto result = Module_Create((MESSAGE_BUS_HANDLE)1, NULL); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_IOTHUBHTTP_02_003: [If configuration->IoTHubName is NULL then IoTHubHttp_Create shall and return NULL.]*/ + TEST_FUNCTION(IoTHubHttp_Create_with_NULL_IoTHubName_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + + ///act + auto result = Module_Create((MESSAGE_BUS_HANDLE)1, &config_with_NULL_IoTHubName); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_IOTHUBHTTP_02_004: [If configuration->IoTHubSuffix is NULL then IoTHubHttp_Create shall fail and return NULL.]*/ + TEST_FUNCTION(IoTHubHttp_Create_with_NULL_IoTHubSuffix_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + + ///act + auto result = Module_Create((MESSAGE_BUS_HANDLE)1, &config_with_NULL_IoTHubSuffix); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_IOTHUBHTTP_02_008: [Otherwise, IoTHubHttp_Create shall return a non-NULL handle.]*/ + /*Tests_SRS_IOTHUBHTTP_02_006: [IoTHubHttp_Create shall create an empty VECTOR containing pointers to PERSONALITYs.]*/ + /*Tests_SRS_IOTHUBHTTP_17_001: [ IoTHubHttp_Create shall create a shared transport by calling IoTHubTransport_Create. ]*/ + /*Tests_SRS_IOTHUBHTTP_02_029: [IoTHubHttp_Create shall create a copy of configuration->IoTHubName.]*/ + /*Tests_SRS_IOTHUBHTTP_02_028: [IoTHubHttp_Create shall create a copy of configuration->IoTHubSuffix.]*/ + //Tests_SRS_IOTHUBHTTP_17_004: [ IoTHubHttp_Create shall store the busHandle. ] + TEST_FUNCTION(IoTHubHttp_Create_succeeds) + { + ///arrange + CIoTHubHTTPMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct("theIoTHub42")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct("theAwesomeSuffix.com")) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, IoTHubTransport_Create(HTTP_Protocol, "theIoTHub42", "theAwesomeSuffix.com")) + .IgnoreAllArguments(); + + ///act + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + + ///assert + ASSERT_IS_NOT_NULL(module); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_027: [When IoTHubHttp_Create encounters an internal failure it shall fail and return NULL.]*/ + TEST_FUNCTION(IoTHubHttp_Create_fails_when_STRING_construct_fails_1) + { + ///arrange + CIoTHubHTTPMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, IoTHubTransport_Create(HTTP_Protocol, "theIoTHub42", "theAwesomeSuffix.com")) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, IoTHubTransport_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct("theIoTHub42")) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + whenShallSTRING_construct_fail = currentSTRING_construct_call + 2; + STRICT_EXPECTED_CALL(mocks, STRING_construct("theAwesomeSuffix")) + .IgnoreArgument(1); + + ///act + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + + ///assert + ASSERT_IS_NULL(module); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_027: [When IoTHubHttp_Create encounters an internal failure it shall fail and return NULL.]*/ + TEST_FUNCTION(IoTHubHttp_Create_fails_when_STRING_construct_fails_2) + { + ///arrange + CIoTHubHTTPMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, IoTHubTransport_Create(HTTP_Protocol, "theIoTHub42", "theAwesomeSuffix.com")) + .IgnoreAllArguments(); + STRICT_EXPECTED_CALL(mocks, IoTHubTransport_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + whenShallSTRING_construct_fail = currentSTRING_construct_call + 1; + STRICT_EXPECTED_CALL(mocks, STRING_construct("theIoTHub42")) + .IgnoreArgument(1); + + + ///act + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + + ///assert + ASSERT_IS_NULL(module); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_17_002: [ If creating the shared transport fails, IoTHubHttp_Create shall fail and return NULL. ]*/ + TEST_FUNCTION(IoTHubHttp_Create_fails_when_transport_create_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, IoTHubTransport_Create(HTTP_Protocol, "theIoTHub42", "theAwesomeSuffix.com")) + .IgnoreAllArguments() + .SetFailReturn((TRANSPORT_HANDLE)NULL); + + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + + ///act + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + + ///assert + ASSERT_IS_NULL(module); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_007: [If creating the VECTOR fails then IoTHubHttp_Create shall fail and return NULL.]*/ + TEST_FUNCTION(IoTHubHttp_Create_fails_when_VECTOR_create_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + whenShallVECTOR_create_fail = currentVECTOR_create_call + 1; + STRICT_EXPECTED_CALL(mocks, VECTOR_create(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + ///act + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + + ///assert + ASSERT_IS_NULL(module); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_IOTHUBHTTP_02_027: [When IoTHubHttp_Create encounters an internal failure it shall fail and return NULL.]*/ + TEST_FUNCTION(IoTHubHttp_Create_fails_when_gballoc_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + + whenShallmalloc_fail = currentmalloc_call + 1; + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + ///act + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + + ///assert + ASSERT_IS_NULL(module); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_IOTHUBHTTP_02_023: [If moduleHandle is NULL then IoTHubHttp_Destroy shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Destroy_with_NULL_returns) + { + ///arrange + CIoTHubHTTPMocks mocks; + + ///act + Module_Destroy(NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_IOTHUBHTTP_02_024: [Otherwise IoTHubHttp_Destroy shall free all used resources.]*/ + TEST_FUNCTION(IoTHubHttp_Destroy_empty_module) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + /* transport handle */ + STRICT_EXPECTED_CALL(mocks, IoTHubTransport_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*IoTHubName cache*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*IoTHubSuffix cache*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + + ///act + Module_Destroy(module); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_IOTHUBHTTP_02_024: [Otherwise IoTHubHttp_Destroy shall free all used resources.]*/ + TEST_FUNCTION(IoTHubHttp_Destroy_module_with_0_identity) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + /*this is IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is the loop trying to dispose of all personalities*/ + /*none for this case*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is VECTOR*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /* transport handle */ + STRICT_EXPECTED_CALL(mocks, IoTHubTransport_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is allocated memory*/ + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + Module_Destroy(module); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup - nothing + } + + /*Tests_SRS_IOTHUBHTTP_02_024: [Otherwise IoTHubHttp_Destroy shall free all used resources.]*/ + TEST_FUNCTION(IoTHubHttp_Destroy_module_with_1_identity) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + (void)Module_Receive(module, MESSAGE_HANDLE_VALID_1); + mocks.ResetAllCalls(); + + /*this is IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is the loop trying to dispose of all personalities*/ + /*1 for this case*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, IoTHubClient_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /* transport handle */ + STRICT_EXPECTED_CALL(mocks, IoTHubTransport_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is VECTOR*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is allocated memory*/ + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + Module_Destroy(module); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup - nothing + } + + /*Tests_SRS_IOTHUBHTTP_02_024: [Otherwise IoTHubHttp_Destroy shall free all used resources.]*/ + TEST_FUNCTION(IoTHubHttp_Destroy_module_with_2_identity) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + (void)Module_Receive(module, MESSAGE_HANDLE_VALID_1); + (void)Module_Receive(module, MESSAGE_HANDLE_VALID_2); + mocks.ResetAllCalls(); + + /*this is IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is the loop trying to dispose of all personalities*/ + /*1 for this case*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_size(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + /*first element*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 0)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, IoTHubClient_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*second element*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_element(IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, IoTHubClient_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /* transport handle */ + STRICT_EXPECTED_CALL(mocks, IoTHubTransport_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is VECTOR*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*this is allocated memory*/ + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + Module_Destroy(module); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup - nothing + } + + /*Tests_SRS_IOTHUBHTTP_02_009: [If moduleHandle or messageHandle is NULL then IoTHubHttp_Receive shall do nothing.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_with_NULL_moduleHandle_returns) + { + ///arrange + CIoTHubHTTPMocks mocks; + + ///act + Module_Receive(NULL, MESSAGE_HANDLE_VALID); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_IOTHUBHTTP_02_009: [If moduleHandle or messageHandle is NULL then IoTHubHttp_Receive shall do nothing.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_with_NULL_messageHandle_returns) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + ///act + Module_Receive(module, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + + } + + /*Tests_SRS_IOTHUBHTTP_02_013: [If the deviceName does not exist in the PERSONALITY collection then IoTHubHttp_Receive shall create a new IOTHUB_CLIENT_HANDLE by a call to IoTHubClient_CreateWithTransport.]*/ + //Tests_SRS_IOTHUBHTTP_17_003: [ If a new PERSONALITY is created, then the IoTHubClient will be set to receive messages, by calling IoTHubClient_SetMessageCallback with callback function IoTHubHttp_ReceiveMessageCallback and PERSONALITY as context.] + /*Tests_SRS_IOTHUBHTTP_02_018: [IoTHubHttp_Receive shall create a new IOTHUB_MESSAGE_HANDLE having the same content as the messageHandle and same properties with the exception of deviceName and deviceKey properties.]*/ + /*Tests_SRS_IOTHUBHTTP_02_020: [IoTHubHttp_Receive shall call IoTHubClient_SendEventAsync passing the IOTHUB_MESSAGE_HANDLE.]*/ + /*Tests_SRS_IOTHUBHTTP_02_022: [IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_succeeds) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("firstDevice")); + /*making a copy of the deviceKey*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("cheiaDeLaPoartaVerde")); + + /*getting the stored IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + /*getting the stored IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*creating the IOTHUB_CLIENT_HANDLE associated with the device*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_CreateWithTransport(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SetMessageCallback(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + } + + /*adding the personality to the VECTOR or personalities*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + /*getting the location of the personality in the VECTOR*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_back(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + { /*scope for creating the IOTHUBMESSAGE from GWMESSAGE*/ + + /*gettng the GW message content*/ + STRICT_EXPECTED_CALL(mocks, Message_GetContent(MESSAGE_HANDLE_VALID_1)); + + /*creating a new IOTHUB_MESSAGE*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_CreateFromByteArray(IGNORED_PTR_ARG, 1)) + .ValidateArgumentBuffer(1, CONSTBUFFER_VALID_CONTENT1.buffer, 1); + + /*every creation has its destruction - this one actually happens AFTER SendEventAsync*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the IOTHUBMESSAGE properties*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW message properties*/ + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW keys and values*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_GetInternals(CONSTMAP_HANDLE_VALID_1, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3) + .IgnoreArgument(4); + + /*add "source"*/ + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, "source", "mapping")) + .IgnoreArgument(1); + + /*add "somethingExtra"*/ + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, "somethingExtra", "blue")) + .IgnoreArgument(1); + } + + /*finally, send the message*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SendEventAsync(IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, NULL)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + + } + + /*Tests_SRS_IOTHUBHTTP_02_017: [If the deviceName exists in the PERSONALITY collection then IoTHubHttp_Receive shall not create a new IOTHUB_CLIENT_HANDLE.]*/ + /*Tests_SRS_IOTHUBHTTP_02_020: [IoTHubHttp_Receive shall call IoTHubClient_SendEventAsync passing the IOTHUB_MESSAGE_HANDLE.]*/ + /*Tests_SRS_IOTHUBHTTP_02_022: [IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_after_receive_succeeds) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. One in this test*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + { /*scope for creating the IOTHUBMESSAGE from GWMESSAGE*/ + + /*gettng the GW message content*/ + STRICT_EXPECTED_CALL(mocks, Message_GetContent(MESSAGE_HANDLE_VALID_1)); + + /*creating a new IOTHUB_MESSAGE*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_CreateFromByteArray(IGNORED_PTR_ARG, 1)) + .ValidateArgumentBuffer(1, CONSTBUFFER_VALID_CONTENT1.buffer, 1); + + /*every creation has its destruction - this one actually happens AFTER SendEventAsync*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the IOTHUBMESSAGE properties*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW message properties*/ + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW keys and values*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_GetInternals(CONSTMAP_HANDLE_VALID_1, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3) + .IgnoreArgument(4); + + /*add "source"*/ + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, "source", "mapping")) + .IgnoreArgument(1); + + /*add "somethingExtra"*/ + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, "somethingExtra", "blue")) + .IgnoreArgument(1); + } + + /*finally, send the message*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SendEventAsync(IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, NULL)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + + } + + /*Tests_SRS_IOTHUBHTTP_02_013: [If the deviceName does not exist in the PERSONALITY collection then IoTHubHttp_Receive shall create a new IOTHUB_CLIENT_HANDLE by a call to IoTHubClient_CreateWithTransport.]*/ + /*Tests_SRS_IOTHUBHTTP_02_018: [IoTHubHttp_Receive shall create a new IOTHUB_MESSAGE_HANDLE having the same content as the messageHandle and same properties with the exception of deviceName and deviceKey properties.]*/ + /*Tests_SRS_IOTHUBHTTP_02_020: [IoTHubHttp_Receive shall call IoTHubClient_SendEventAsync passing the IOTHUB_MESSAGE_HANDLE.]*/ + /*Tests_SRS_IOTHUBHTTP_02_022: [IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_after_Receive_a_new_device_succeeds) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_2)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_2, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_2, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_2, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. One in this test*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("secondDevice")); + /*making a copy of the deviceKey*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("red")); + + /*getting the stored IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + /*getting the stored IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*creating the IOTHUB_CLIENT_HANDLE associated with the device*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_CreateWithTransport(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SetMessageCallback(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + } + + /*adding the personality to the VECTOR or personalities*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + /*getting the location of the personality in the VECTOR*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_back(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + { /*scope for creating the IOTHUBMESSAGE from GWMESSAGE*/ + + /*gettng the GW message content*/ + STRICT_EXPECTED_CALL(mocks, Message_GetContent(MESSAGE_HANDLE_VALID_2)); + + /*creating a new IOTHUB_MESSAGE*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_CreateFromByteArray(IGNORED_PTR_ARG, 0)) + ; + + /*every creation has its destruction - this one actually happens AFTER SendEventAsync*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the IOTHUBMESSAGE properties*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW message properties*/ + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW keys and values*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_GetInternals(CONSTMAP_HANDLE_VALID_2, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3) + .IgnoreArgument(4); + + /*add "source"*/ + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, "source", "mapping")) + .IgnoreArgument(1); + } + + /*finally, send the message*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SendEventAsync(IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, NULL)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_2); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + + } + + /*Tests_SRS_IOTHUBHTTP_02_021: [If IoTHubClient_SendEventAsync fails then IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_IoTHubClient_SendEventAsync_fails_it_still_returns) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("firstDevice")); + /*making a copy of the deviceKey*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("cheiaDeLaPoartaVerde")); + + /*getting the stored IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + /*getting the stored IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*creating the IOTHUB_CLIENT_HANDLE associated with the device*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_CreateWithTransport(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SetMessageCallback(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + } + + /*adding the personality to the VECTOR or personalities*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + /*getting the location of the personality in the VECTOR*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_back(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + { /*scope for creating the IOTHUBMESSAGE from GWMESSAGE*/ + + /*gettng the GW message content*/ + STRICT_EXPECTED_CALL(mocks, Message_GetContent(MESSAGE_HANDLE_VALID_1)); + + /*creating a new IOTHUB_MESSAGE*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_CreateFromByteArray(IGNORED_PTR_ARG, 1)) + .ValidateArgumentBuffer(1, CONSTBUFFER_VALID_CONTENT1.buffer, 1); + + /*every creation has its destruction - this one actually happens AFTER SendEventAsync*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the IOTHUBMESSAGE properties*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW message properties*/ + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW keys and values*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_GetInternals(CONSTMAP_HANDLE_VALID_1, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3) + .IgnoreArgument(4); + + /*add "source"*/ + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, "source", "mapping")) + .IgnoreArgument(1); + + /*add "somethingExtra"*/ + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, "somethingExtra", "blue")) + .IgnoreArgument(1); + } + + /*finally, send the message*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SendEventAsync(IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, NULL)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetReturn(IOTHUB_CLIENT_ERROR); + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + + } + + /*Tests_SRS_IOTHUBHTTP_02_019: [If creating the IOTHUB_MESSAGE_HANDLE fails, then IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_creating_IOTHUB_MESSAGE_HANDLE_fails_1) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("firstDevice")); + /*making a copy of the deviceKey*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("cheiaDeLaPoartaVerde")); + + /*getting the stored IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + /*getting the stored IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*creating the IOTHUB_CLIENT_HANDLE associated with the device*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_CreateWithTransport(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SetMessageCallback(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + } + + /*adding the personality to the VECTOR or personalities*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + /*getting the location of the personality in the VECTOR*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_back(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + { /*scope for creating the IOTHUBMESSAGE from GWMESSAGE*/ + + /*gettng the GW message content*/ + STRICT_EXPECTED_CALL(mocks, Message_GetContent(MESSAGE_HANDLE_VALID_1)); + + /*creating a new IOTHUB_MESSAGE*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_CreateFromByteArray(IGNORED_PTR_ARG, 1)) + .ValidateArgumentBuffer(1, CONSTBUFFER_VALID_CONTENT1.buffer, 1); + + /*every creation has its destruction - this one actually happens AFTER SendEventAsync*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the IOTHUBMESSAGE properties*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW message properties*/ + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW keys and values*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_GetInternals(CONSTMAP_HANDLE_VALID_1, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3) + .IgnoreArgument(4); + + /*add "source"*/ + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, "source", "mapping")) + .IgnoreArgument(1); + + /*add "somethingExtra"*/ + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, "somethingExtra", "blue")) + .IgnoreArgument(1) + .SetReturn(MAP_ERROR); + } + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + + } + + /*Tests_SRS_IOTHUBHTTP_02_019: [If creating the IOTHUB_MESSAGE_HANDLE fails, then IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_creating_IOTHUB_MESSAGE_HANDLE_fails_2) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("firstDevice")); + /*making a copy of the deviceKey*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("cheiaDeLaPoartaVerde")); + + /*getting the stored IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + /*getting the stored IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*creating the IOTHUB_CLIENT_HANDLE associated with the device*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_CreateWithTransport(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SetMessageCallback(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + } + + /*adding the personality to the VECTOR or personalities*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + /*getting the location of the personality in the VECTOR*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_back(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + { /*scope for creating the IOTHUBMESSAGE from GWMESSAGE*/ + + /*gettng the GW message content*/ + STRICT_EXPECTED_CALL(mocks, Message_GetContent(MESSAGE_HANDLE_VALID_1)); + + /*creating a new IOTHUB_MESSAGE*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_CreateFromByteArray(IGNORED_PTR_ARG, 1)) + .ValidateArgumentBuffer(1, CONSTBUFFER_VALID_CONTENT1.buffer, 1); + + /*every creation has its destruction - this one actually happens AFTER SendEventAsync*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the IOTHUBMESSAGE properties*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW message properties*/ + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW keys and values*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_GetInternals(CONSTMAP_HANDLE_VALID_1, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3) + .IgnoreArgument(4); + + /*add "source"*/ + STRICT_EXPECTED_CALL(mocks, Map_AddOrUpdate(IGNORED_PTR_ARG, "source", "mapping")) + .IgnoreArgument(1) + .SetReturn(MAP_ERROR); + } + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + + } + + /*Tests_SRS_IOTHUBHTTP_02_019: [If creating the IOTHUB_MESSAGE_HANDLE fails, then IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_creating_IOTHUB_MESSAGE_HANDLE_fails_3) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("firstDevice")); + /*making a copy of the deviceKey*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("cheiaDeLaPoartaVerde")); + + /*getting the stored IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + /*getting the stored IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*creating the IOTHUB_CLIENT_HANDLE associated with the device*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_CreateWithTransport(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SetMessageCallback(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + } + + /*adding the personality to the VECTOR or personalities*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + /*getting the location of the personality in the VECTOR*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_back(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + { /*scope for creating the IOTHUBMESSAGE from GWMESSAGE*/ + + /*gettng the GW message content*/ + STRICT_EXPECTED_CALL(mocks, Message_GetContent(MESSAGE_HANDLE_VALID_1)); + + /*creating a new IOTHUB_MESSAGE*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_CreateFromByteArray(IGNORED_PTR_ARG, 1)) + .ValidateArgumentBuffer(1, CONSTBUFFER_VALID_CONTENT1.buffer, 1); + + /*every creation has its destruction - this one actually happens AFTER SendEventAsync*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the IOTHUBMESSAGE properties*/ + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW message properties*/ + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*getting the GW keys and values*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_GetInternals(CONSTMAP_HANDLE_VALID_1, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3) + .IgnoreArgument(4) + .SetReturn(CONSTMAP_ERROR); + } + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + + } + + /*Tests_SRS_IOTHUBHTTP_02_019: [If creating the IOTHUB_MESSAGE_HANDLE fails, then IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_creating_IOTHUB_MESSAGE_HANDLE_fails_4) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("firstDevice")); + /*making a copy of the deviceKey*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("cheiaDeLaPoartaVerde")); + + /*getting the stored IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + /*getting the stored IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*creating the IOTHUB_CLIENT_HANDLE associated with the device*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_CreateWithTransport(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SetMessageCallback(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + } + + /*adding the personality to the VECTOR or personalities*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + /*getting the location of the personality in the VECTOR*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_back(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + { /*scope for creating the IOTHUBMESSAGE from GWMESSAGE*/ + + /*gettng the GW message content*/ + STRICT_EXPECTED_CALL(mocks, Message_GetContent(MESSAGE_HANDLE_VALID_1)); + + /*creating a new IOTHUB_MESSAGE*/ + whenShallIoTHubMessage_CreateFromByteArray_fail = currentIoTHubMessage_CreateFromByteArray_call + 1; + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_CreateFromByteArray(IGNORED_PTR_ARG, 1)) + .ValidateArgumentBuffer(1, CONSTBUFFER_VALID_CONTENT1.buffer, 1) + ; + } + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_016: [If adding the new triplet fails, then IoTHubClient_Create shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_adding_the_personality_fails_it_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("firstDevice")); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceKey*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("cheiaDeLaPoartaVerde")); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) /*this is deviceName*/ + .IgnoreArgument(1); + + /*getting the stored IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + /*getting the stored IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*creating the IOTHUB_CLIENT_HANDLE associated with the device*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_CreateWithTransport(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, IoTHubClient_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SetMessageCallback(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3); + } + + /*adding the personality to the VECTOR or personalities*/ + whenShallVECTOR_push_back_fail = currentVECTOR_push_back_call + 1; + STRICT_EXPECTED_CALL(mocks, VECTOR_push_back(IGNORED_PTR_ARG, IGNORED_PTR_ARG, 1)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_014: [If creating the PERSONALITY fails then IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_creating_the_personality_fails_it_fails_1a) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("firstDevice")) + .SetFailReturn((STRING_HANDLE)NULL); + + } + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_014: [If creating the PERSONALITY fails then IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_creating_the_personality_fails_it_fails_1b) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("firstDevice")); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceKey*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("cheiaDeLaPoartaVerde")); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) /*this is deviceName*/ + .IgnoreArgument(1); + + /*getting the stored IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + /*getting the stored IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*creating the IOTHUB_CLIENT_HANDLE associated with the device*/ + whenShallIoTHubClient_Create_fail = currentIoTHubClient_Create_call + 1; + STRICT_EXPECTED_CALL(mocks, IoTHubClient_CreateWithTransport(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + + } + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_014: [If creating the PERSONALITY fails then IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_creating_the_personality_fails_it_fails_1c) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("firstDevice")); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceKey*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("cheiaDeLaPoartaVerde")); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) /*this is deviceName*/ + .IgnoreArgument(1); + + /*getting the stored IoTHubName*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + /*getting the stored IoTHubSuffix*/ + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*creating the IOTHUB_CLIENT_HANDLE associated with the device*/ + STRICT_EXPECTED_CALL(mocks, IoTHubClient_CreateWithTransport(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, IoTHubClient_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, IoTHubClient_SetMessageCallback(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(3) + .SetFailReturn(IOTHUB_CLIENT_ERROR); + } + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_014: [If creating the PERSONALITY fails then IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_creating_the_personality_fails_it_fails_2) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceName*/ + STRICT_EXPECTED_CALL(mocks, STRING_construct("firstDevice")); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + /*making a copy of the deviceKey*/ + whenShallSTRING_construct_fail = currentSTRING_construct_call + 2; + STRICT_EXPECTED_CALL(mocks, STRING_construct("cheiaDeLaPoartaVerde")); + } + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_014: [If creating the PERSONALITY fails then IoTHubHttp_Receive shall return.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_creating_the_personality_fails_it_fails_3) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")); + + /*VECTOR_find_if incurs a STRING_c_str until it find the deviceName. None in this test*/ + STRICT_EXPECTED_CALL(mocks, VECTOR_find_if(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreAllArguments(); + + /*because the deviceName is brand new, it will be added as a new personality*/ + {/*separate scope for personality building*/ + /* create a new PERSONALITY */ + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + } + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_012: [If message properties do not contain a property called "deviceKey" having a non-NULL value then IoTHubHttp_Receive shall do nothing.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_deviceKey_doesn_t_exist_returns) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceKey")) + .SetReturn((const char*)NULL); + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_011: [If message properties do not contain a property called "deviceName" having a non-NULL value then IoTHubHttp_Receive shall do nothing.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_deviceName_doesn_t_exist_returns) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "deviceName")) + .SetReturn((const char*)NULL); + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + /*Tests_SRS_IOTHUBHTTP_02_010: [If message properties do not contain a property called "source" having the value set to "mapping" then IoTHubHttp_Receive shall do nothing.]*/ + TEST_FUNCTION(IoTHubHttp_Receive_when_source_mapping_doesn_t_exist_returns) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(MESSAGE_HANDLE_VALID_1)); + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_GetValue(CONSTMAP_HANDLE_VALID_1, "source")) + .SetReturn((const char*)NULL); + + ///act + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + //Tests_SRS_IOTHUBHTTP_17_007: [ IoTHubHttp_ReceiveMessageCallback shall get properties from message by calling IoTHubMessage_Properties. ] + //Tests_SRS_IOTHUBHTTP_17_009: [ IoTHubHttp_ReceiveMessageCallback shall define a property "source" as "IoTHubHTTP". ] + //Tests_SRS_IOTHUBHTTP_17_010: [ IoTHubHttp_ReceiveMessageCallback shall define a property "deviceName" as the PERSONALITY's deviceName. ] + //Tests_SRS_IOTHUBHTTP_17_011: [ IoTHubHttp_ReceiveMessageCallback shall combine message properties with the "source" and "deviceName" properties. ] + //Tests_SRS_IOTHUBHTTP_17_014: [ If Message content type is IOTHUBMESSAGE_STRING, IoTHubHttp_ReceiveMessageCallback shall get the buffer from results of IoTHubMessage_GetString. ] + //Tests_SRS_IOTHUBHTTP_17_015: [ If Message content type is IOTHUBMESSAGE_STRING, IoTHubHttp_ReceiveMessageCallback shall get the buffer size from the string length. ] + //Tests_SRS_IOTHUBHTTP_17_016: [ IoTHubHttp_ReceiveMessageCallback shall create a new message from combined properties, the size and buffer. ] + //Tests_SRS_IOTHUBHTTP_17_018: [ IoTHubHttp_ReceiveMessageCallback shall call MessageBus_Publish with the new message and the busHandle. ] + //Tests_SRS_IOTHUBHTTP_17_020: [ IoTHubHttp_ReceiveMessageCallback shall destroy all resources it creates. ] + //Tests_SRS_IOTHUBHTTP_17_021: [ Upon success, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ACCEPTED. ] + TEST_FUNCTION(IoTHubHttp_callback_string_success) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + IOTHUB_MESSAGE_HANDLE hubMsg = IOTHUB_MESSAGE_HANDLE_VALID; + mocks.ResetAllCalls(); + + IoTHubHttp_receive_message_content = "a message"; + IoTHubHttp_receive_message_size = 9; + + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetContentType(hubMsg)) + .SetReturn(IOTHUBMESSAGE_STRING); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetString(hubMsg)); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(hubMsg)) + .SetReturn(MAP_HANDLE_VALID_1); + STRICT_EXPECTED_CALL(mocks, Map_Add(MAP_HANDLE_VALID_1, GW_SOURCE_PROPERTY, GW_IOTHUB_MODULE)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(MAP_HANDLE_VALID_1, GW_DEVICENAME_PROPERTY, "firstDevice")); + STRICT_EXPECTED_CALL(mocks, Message_Create(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Publish(MESSAGE_BUS_HANDLE_VALID, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + + // IoTHubHttp_receive_message_callback_function and IoTHubHttp_receive_message_userContext set in mock + auto result = IoTHubHttp_receive_message_callback_function(hubMsg, IoTHubHttp_receive_message_userContext); + + + ///assert + ASSERT_ARE_EQUAL(IOTHUBMESSAGE_DISPOSITION_RESULT, result, IOTHUBMESSAGE_ACCEPTED); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + //Tests_SRS_IOTHUBHTTP_17_007: [ IoTHubHttp_ReceiveMessageCallback shall get properties from message by calling IoTHubMessage_Properties. ] + //Tests_SRS_IOTHUBHTTP_17_009: [ IoTHubHttp_ReceiveMessageCallback shall define a property "source" as "IoTHubHTTP". ] + //Tests_SRS_IOTHUBHTTP_17_010: [ IoTHubHttp_ReceiveMessageCallback shall define a property "deviceName" as the PERSONALITY's deviceName. ] + //Tests_SRS_IOTHUBHTTP_17_011: [ IoTHubHttp_ReceiveMessageCallback shall combine message properties with the "source" and "deviceName" properties. ] + //Tests_SRS_IOTHUBHTTP_17_013: [ If Message content type is IOTHUBMESSAGE_BYTEARRAY, IoTHubHttp_ReceiveMessageCallback shall get the size and buffer from the results of IoTHubMessage_GetByteArray. ] + //Tests_SRS_IOTHUBHTTP_17_016: [ IoTHubHttp_ReceiveMessageCallback shall create a new message from combined properties, the size and buffer. ] + //Tests_SRS_IOTHUBHTTP_17_018: [ IoTHubHttp_ReceiveMessageCallback shall call MessageBus_Publish with the new message and the busHandle. ] + //Tests_SRS_IOTHUBHTTP_17_020: [ IoTHubHttp_ReceiveMessageCallback shall destroy all resources it creates. ] + //Tests_SRS_IOTHUBHTTP_17_021: [ Upon success, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ACCEPTED. ] + TEST_FUNCTION(IoTHubHttp_callback_byte_array_success) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + IOTHUB_MESSAGE_HANDLE hubMsg = IOTHUB_MESSAGE_HANDLE_VALID; + mocks.ResetAllCalls(); + + IoTHubHttp_receive_message_content = "a message"; + IoTHubHttp_receive_message_size = 9; + + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetContentType(hubMsg)) + .SetReturn(IOTHUBMESSAGE_BYTEARRAY); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetByteArray(hubMsg, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(hubMsg)) + .SetReturn(MAP_HANDLE_VALID_1); + STRICT_EXPECTED_CALL(mocks, Map_Add(MAP_HANDLE_VALID_1, GW_SOURCE_PROPERTY, GW_IOTHUB_MODULE)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(MAP_HANDLE_VALID_1, GW_DEVICENAME_PROPERTY, "firstDevice")); + STRICT_EXPECTED_CALL(mocks, Message_Create(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Publish(MESSAGE_BUS_HANDLE_VALID, IGNORED_PTR_ARG)) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + + // IoTHubHttp_receive_message_callback_function and IoTHubHttp_receive_message_userContext set in mock + auto result = IoTHubHttp_receive_message_callback_function(hubMsg, IoTHubHttp_receive_message_userContext); + + + ///assert + ASSERT_ARE_EQUAL(IOTHUBMESSAGE_DISPOSITION_RESULT, result, IOTHUBMESSAGE_ACCEPTED); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + //Tests_SRS_IOTHUBHTTP_17_019: [ If the message fails to publish, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_REJECTED. ] + TEST_FUNCTION(IoTHubHttp_callback_bus_publish_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + IOTHUB_MESSAGE_HANDLE hubMsg = IOTHUB_MESSAGE_HANDLE_VALID; + mocks.ResetAllCalls(); + + IoTHubHttp_receive_message_content = "a message"; + IoTHubHttp_receive_message_size = 9; + + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetContentType(hubMsg)) + .SetReturn(IOTHUBMESSAGE_BYTEARRAY); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetByteArray(hubMsg, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(hubMsg)) + .SetReturn(MAP_HANDLE_VALID_1); + STRICT_EXPECTED_CALL(mocks, Map_Add(MAP_HANDLE_VALID_1, GW_SOURCE_PROPERTY, GW_IOTHUB_MODULE)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(MAP_HANDLE_VALID_1, GW_DEVICENAME_PROPERTY, "firstDevice")); + STRICT_EXPECTED_CALL(mocks, Message_Create(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, MessageBus_Publish(MESSAGE_BUS_HANDLE_VALID, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .SetFailReturn(MESSAGE_BUS_ERROR); + STRICT_EXPECTED_CALL(mocks, Message_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + ///act + + // IoTHubHttp_receive_message_callback_function and IoTHubHttp_receive_message_userContext set in mock + auto result = IoTHubHttp_receive_message_callback_function(hubMsg, IoTHubHttp_receive_message_userContext); + + + ///assert + ASSERT_ARE_EQUAL(IOTHUBMESSAGE_DISPOSITION_RESULT, result, IOTHUBMESSAGE_REJECTED); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + //Tests_SRS_IOTHUBHTTP_17_017: [ If the message fails to create, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_REJECTED. ] + TEST_FUNCTION(IoTHubHttp_callback_message_create_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + IOTHUB_MESSAGE_HANDLE hubMsg = IOTHUB_MESSAGE_HANDLE_VALID; + mocks.ResetAllCalls(); + + IoTHubHttp_receive_message_content = "a message"; + IoTHubHttp_receive_message_size = 9; + + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetContentType(hubMsg)) + .SetReturn(IOTHUBMESSAGE_BYTEARRAY); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetByteArray(hubMsg, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(hubMsg)) + .SetReturn(MAP_HANDLE_VALID_1); + STRICT_EXPECTED_CALL(mocks, Map_Add(MAP_HANDLE_VALID_1, GW_SOURCE_PROPERTY, GW_IOTHUB_MODULE)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(MAP_HANDLE_VALID_1, GW_DEVICENAME_PROPERTY, "firstDevice")); + STRICT_EXPECTED_CALL(mocks, Message_Create(IGNORED_PTR_ARG)) + .IgnoreArgument(1) + .SetFailReturn((MESSAGE_HANDLE)NULL); + + ///act + + // IoTHubHttp_receive_message_callback_function and IoTHubHttp_receive_message_userContext set in mock + auto result = IoTHubHttp_receive_message_callback_function(hubMsg, IoTHubHttp_receive_message_userContext); + + + ///assert + ASSERT_ARE_EQUAL(IOTHUBMESSAGE_DISPOSITION_RESULT, result, IOTHUBMESSAGE_REJECTED); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + //Tests_SRS_IOTHUBHTTP_17_022: [ If message properties fail to combined, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ] + TEST_FUNCTION(IoTHubHttp_callback_Map_Add_2_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + IOTHUB_MESSAGE_HANDLE hubMsg = IOTHUB_MESSAGE_HANDLE_VALID; + mocks.ResetAllCalls(); + + IoTHubHttp_receive_message_content = "a message"; + IoTHubHttp_receive_message_size = 9; + + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetContentType(hubMsg)) + .SetReturn(IOTHUBMESSAGE_BYTEARRAY); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetByteArray(hubMsg, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(hubMsg)) + .SetReturn(MAP_HANDLE_VALID_1); + STRICT_EXPECTED_CALL(mocks, Map_Add(MAP_HANDLE_VALID_1, GW_SOURCE_PROPERTY, GW_IOTHUB_MODULE)); + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Add(MAP_HANDLE_VALID_1, GW_DEVICENAME_PROPERTY, "firstDevice")) + .SetFailReturn(MAP_ERROR); + + + ///act + + // IoTHubHttp_receive_message_callback_function and IoTHubHttp_receive_message_userContext set in mock + auto result = IoTHubHttp_receive_message_callback_function(hubMsg, IoTHubHttp_receive_message_userContext); + + + ///assert + ASSERT_ARE_EQUAL(IOTHUBMESSAGE_DISPOSITION_RESULT, result, IOTHUBMESSAGE_ABANDONED); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + //Tests_SRS_IOTHUBHTTP_17_022: [ If message properties fail to combined, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ] + TEST_FUNCTION(IoTHubHttp_callback_Map_Add_1_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + IOTHUB_MESSAGE_HANDLE hubMsg = IOTHUB_MESSAGE_HANDLE_VALID; + mocks.ResetAllCalls(); + + IoTHubHttp_receive_message_content = "a message"; + IoTHubHttp_receive_message_size = 9; + + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetContentType(hubMsg)) + .SetReturn(IOTHUBMESSAGE_BYTEARRAY); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetByteArray(hubMsg, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(hubMsg)) + .SetReturn(MAP_HANDLE_VALID_1); + STRICT_EXPECTED_CALL(mocks, Map_Add(MAP_HANDLE_VALID_1, GW_SOURCE_PROPERTY, GW_IOTHUB_MODULE)) + .SetFailReturn(MAP_ERROR); + + + ///act + + // IoTHubHttp_receive_message_callback_function and IoTHubHttp_receive_message_userContext set in mock + auto result = IoTHubHttp_receive_message_callback_function(hubMsg, IoTHubHttp_receive_message_userContext); + + + ///assert + ASSERT_ARE_EQUAL(IOTHUBMESSAGE_DISPOSITION_RESULT, result, IOTHUBMESSAGE_ABANDONED); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + //Tests_SRS_IOTHUBHTTP_17_008: [ If message properties are NULL, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ] + TEST_FUNCTION(IoTHubHttp_callback_message_properties_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + IOTHUB_MESSAGE_HANDLE hubMsg = IOTHUB_MESSAGE_HANDLE_VALID; + mocks.ResetAllCalls(); + + IoTHubHttp_receive_message_content = "a message"; + IoTHubHttp_receive_message_size = 9; + + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetContentType(hubMsg)) + .SetReturn(IOTHUBMESSAGE_BYTEARRAY); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetByteArray(hubMsg, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_Properties(hubMsg)) + .SetReturn(MAP_HANDLE_VALID_1) + .SetFailReturn((MAP_HANDLE)NULL); + + ///act + + // IoTHubHttp_receive_message_callback_function and IoTHubHttp_receive_message_userContext set in mock + auto result = IoTHubHttp_receive_message_callback_function(hubMsg, IoTHubHttp_receive_message_userContext); + + + ///assert + ASSERT_ARE_EQUAL(IOTHUBMESSAGE_DISPOSITION_RESULT, result, IOTHUBMESSAGE_ABANDONED); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + //Tests_SRS_IOTHUBHTTP_17_023: [ If IoTHubMessage_GetByteArray fails, IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ] + TEST_FUNCTION(IoTHubHttp_callback_message_get_byte_array_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + IOTHUB_MESSAGE_HANDLE hubMsg = IOTHUB_MESSAGE_HANDLE_VALID; + mocks.ResetAllCalls(); + + IoTHubHttp_receive_message_content = "a message"; + IoTHubHttp_receive_message_size = 9; + + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetContentType(hubMsg)) + .SetReturn(IOTHUBMESSAGE_BYTEARRAY); + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetByteArray(hubMsg, IGNORED_PTR_ARG, IGNORED_PTR_ARG)) + .IgnoreArgument(2) + .IgnoreArgument(3) + .SetFailReturn(IOTHUB_MESSAGE_ERROR); + + ///act + + // IoTHubHttp_receive_message_callback_function and IoTHubHttp_receive_message_userContext set in mock + auto result = IoTHubHttp_receive_message_callback_function(hubMsg, IoTHubHttp_receive_message_userContext); + + + ///assert + ASSERT_ARE_EQUAL(IOTHUBMESSAGE_DISPOSITION_RESULT, result, IOTHUBMESSAGE_ABANDONED); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + //Tests_SRS_IOTHUBHTTP_17_006: [ If Message Content type is IOTHUBMESSAGE_UNKNOWN, then IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ] + TEST_FUNCTION(IoTHubHttp_callback_message_content_unknown_fails) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + IOTHUB_MESSAGE_HANDLE hubMsg = IOTHUB_MESSAGE_HANDLE_VALID; + mocks.ResetAllCalls(); + + IoTHubHttp_receive_message_content = "a message"; + IoTHubHttp_receive_message_size = 9; + + STRICT_EXPECTED_CALL(mocks, IoTHubMessage_GetContentType(hubMsg)) + .SetReturn(IOTHUBMESSAGE_UNKNOWN); + + ///act + + // IoTHubHttp_receive_message_callback_function and IoTHubHttp_receive_message_userContext set in mock + auto result = IoTHubHttp_receive_message_callback_function(hubMsg, IoTHubHttp_receive_message_userContext); + + + ///assert + ASSERT_ARE_EQUAL(IOTHUBMESSAGE_DISPOSITION_RESULT, result, IOTHUBMESSAGE_ABANDONED); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + + //Tests_SRS_IOTHUBHTTP_17_005: [ If userContextCallback is NULL, then IoTHubHttp_ReceiveMessageCallback shall return IOTHUBMESSAGE_ABANDONED. ] + TEST_FUNCTION(IoTHubHttp_callback_message_user_context_null) + { + ///arrange + CIoTHubHTTPMocks mocks; + auto module = Module_Create(MESSAGE_BUS_HANDLE_VALID, &config_valid); + Module_Receive(module, MESSAGE_HANDLE_VALID_1); + IOTHUB_MESSAGE_HANDLE hubMsg = IOTHUB_MESSAGE_HANDLE_VALID; + mocks.ResetAllCalls(); + + IoTHubHttp_receive_message_content = "a message"; + IoTHubHttp_receive_message_size = 9; + + + ///act + + // IoTHubHttp_receive_message_callback_function and IoTHubHttp_receive_message_userContext set in mock + auto result = IoTHubHttp_receive_message_callback_function(hubMsg, NULL); + + + ///assert + ASSERT_ARE_EQUAL(IOTHUBMESSAGE_DISPOSITION_RESULT, result, IOTHUBMESSAGE_ABANDONED); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Module_Destroy(module); + } + +END_TEST_SUITE(iothubhttp_unittests) diff --git a/modules/iothubhttp/tests/iothubhttp_unittests/main.c b/modules/iothubhttp/tests/iothubhttp_unittests/main.c new file mode 100644 index 00000000..b04de411 --- /dev/null +++ b/modules/iothubhttp/tests/iothubhttp_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(iothubhttp_unittests, failedTestCount); + return failedTestCount; +} diff --git a/modules/logger/CMakeLists.txt b/modules/logger/CMakeLists.txt new file mode 100644 index 00000000..c7370bff --- /dev/null +++ b/modules/logger/CMakeLists.txt @@ -0,0 +1,76 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for the logger module +cmake_minimum_required(VERSION 2.8.11) + +set(logger_sources + ./src/logger.c +) + +set(logger_headers + ./inc/logger.h +) + +set(logger_static_sources + ${logger_sources} +) + +set(logger_static_headers + ${logger_headers} +) + +set(logger_hl_sources + ./src/logger_hl.c +) + +set(logger_hl_headers + ./inc/logger_hl.h +) + +set(logger_hl_static_sources + ${logger_hl_sources} +) + +set(logger_hl_static_headers + ${logger_hl_headers} +) + + +include_directories(./inc) +include_directories(${GW_INC}) + +#this builds the Logger dynamic library +add_library(logger MODULE ${logger_sources} ${logger_headers}) +target_link_libraries(logger gateway) + +#this builds the Logger static library +add_library(logger_static ${logger_static_sources} ${logger_static_headers}) +target_compile_definitions(logger_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(logger_static gateway) + +#this builds the Logger_HL dynamic library (by default it uses Logger linked statically) +add_library(logger_hl MODULE ${logger_hl_sources} ${logger_hl_headers}) +target_link_libraries(logger_hl logger_static gateway) + +#this builds the Logger_HL static library (by default it uses Logger linked statically) +add_library(logger_hl_static ${logger_hl_static_sources} ${logger_hl_static_headers}) +target_compile_definitions(logger_hl_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(logger_hl_static logger_static gateway) + +linkSharedUtil(logger) +linkSharedUtil(logger_static) +linkSharedUtil(logger_hl) +linkSharedUtil(logger_hl_static) + +add_module_to_solution(logger) + +add_subdirectory(tests) + + + +if(install_executables) + install(TARGETS logger LIBRARY DESTINATION lib) + install(TARGETS logger_hl LIBRARY DESTINATION lib) +endif() + diff --git a/modules/logger/README.md b/modules/logger/README.md new file mode 100644 index 00000000..3b2b5480 --- /dev/null +++ b/modules/logger/README.md @@ -0,0 +1 @@ +# Logger Module \ No newline at end of file diff --git a/modules/logger/devdoc/logger.md b/modules/logger/devdoc/logger.md new file mode 100644 index 00000000..585657d1 --- /dev/null +++ b/modules/logger/devdoc/logger.md @@ -0,0 +1,119 @@ +LOGGER MODULE +=========== + +High level design +----------------- + +### Overview + +This module logs all the received traffic. The module has no filtering, so it logs everything into a file. The file contains a JSON object. THe JSON object +is an array of individual JSON values. There are 2 types of such JSON values: markers for being/end of logging and effective log data. + +####Additional data types +```c +typedef enum LOGGER_TYPE_TAG +{ + LOGGING_TO_FILE +}LOGGER_TYPE; + +typedef struct LOGGER_CONFIG_TAG +{ + LOGGER_TYPE selector; + union + { + struct LOGGER_CONFIG_FILE_TAG + { + const char* name; + } loggerConfigFile; + }selectee; +}LOGGER_CONFIG; +``` + +###Logger_Create +```c +MODULE_HANDLE Logger_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +``` +Creates a new LOGGER instance. `configuration` is a pointer to a `LOGGER_CONFIG`. + +**SRS_LOGGER_02_001: [**If busHandle is NULL then Logger_Create shall fail and return NULL.**]** +**SRS_LOGGER_02_002: [**If configuration is NULL then Logger_Create shall fail and return NULL.**]** +**SRS_LOGGER_02_003: [**If configuration->selector has a value different than `LOGGING_TO_FILE` then Logger_Create shall fail and return NULL.**]** +**SRS_LOGGER_02_004: [**If configuration->selectee.loggerConfigFile.name is NULL then Logger_Create shall fail and return NULL.**]** + +**SRS_LOGGER_02_005: [**Logger_Create shall allocate memory for the below structure.**]** + +```c +typedef LOGGER_HANDLE_DATA_TAG +{ + FILE* fout; +}LOGGER_HANDLE_DATA; +``` +**SRS_LOGGER_02_020: [**If the file selectee.loggerConfigFile.name does not exist, it shall be created.**]** +**SRS_LOGGER_02_021: [**If creating selectee.loggerConfigFile.name fails then Logger_Create shall fail and return NULL.**]** + +**SRS_LOGGER_02_006: [**Logger_Create shall open the file configuration the filename selectee.loggerConfigFile.name in update (reading and writing) mode and assign the result of fopen +to fout field. +**]** +**SRS_LOGGER_02_017: [**Logger_Create shall add the following JSON value to the existing array of JSON values in the file:**]** +```json +{ + "time":"timeAsPrinted by strftime", + "content": "Log started" +} +``` +**SRS_LOGGER_02_018: [**If the file does not contain a JSON array, then it shall create it.**]** + +**SRS_LOGGER_02_007: [**If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.**]** + +**SRS_LOGGER_02_008: [**Otherwise Logger_Create shall return a non-NULL pointer.**]** + +###Logger_Receive +```c +void Logger_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); +``` + +**SRS_LOGGER_02_009: [**If moduleHandle is NULL then Logger_Receive shall fail and return.**]** +**SRS_LOGGER_02_010: [**If messageHandle is NULL then Logger_Receive shall fail and return.**]** +**SRS_LOGGER_02_011: [**Logger_Receive shall write in the fout FILE the following information in JSON format:**]** +```json +[ + { + "received":"timeAsPrintedBy strftime" + "properties": + { + "property1":"value1", + "property2":"value2" + }, + "content":"base64 encode of the message content" + }, + { + //next message here. + } +] +``` + +**SRS_LOGGER_02_012: [**If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.**]** + +**SRS_LOGGER_02_013: [**Logger_Receive shall return.**]** + + +###Logger_Destroy +```c +void Logger_Destroy(MODULE_HANDLE moduleHandle); +``` +**SRS_LOGGER_02_014: [**If moduleHandle is NULL then Logger_Destroy shall return.**]** +**SRS_LOGGER_02_019: [**Logger_Destroy shall add to the log file the following end of log JSON object:**]** +```json +{ + "time":"timeAsPrinted by ctime", + "content": "Log stopped" +} +``` +**SRS_LOGGER_02_015: [**Otherwise Logger_Destroy shall unuse all used resources.**]** + + +###Module_GetAPIs +```c +extern const MODULE_APIS* Module_GetAPIS(void); +``` +**SRS_LOGGER_02_016: [**Module_GetAPIS shall return a non-NULL pointer to a structure of type MODULE_APIS that has all fields non-NULL.**]** \ No newline at end of file diff --git a/modules/logger/devdoc/logger_hl.md b/modules/logger/devdoc/logger_hl.md new file mode 100644 index 00000000..a7871ce1 --- /dev/null +++ b/modules/logger/devdoc/logger_hl.md @@ -0,0 +1,75 @@ +LOGGER HL MODULE +=============== + +High level design +----------------- + +### References +[logger](./logger.md) + +[json](http://www.json.org) + +[gateway](../../../../devdoc/gateway_requirements.md) + +### Overview + +This module is just a passthrough to LOGGER MODULE in all aspects except for creation that is done by means of a json value. + + +###Logger_HL_Create +```c +MODULE_HANDLE Logger_HL_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +``` +Creates a new LOGGER MODULE HL instance. `configuration` is a pointer to a const char* that contains a json object as supplied by `Gateway_Create_From_JSON`. +By convention in the json object should contain +```json +{"filename":"nameOfTheFile"} +``` +where "nameOfTheFile" is the output log file name. + +Example: +The following Gateway config file will contain a module called "logger_hl" build from F:\logger_hl.dll and instructs the logger_hl to output to +the file deviceCloudUploadGatewaylog.txt +```json +{ + "modules" : + [ + { + "module name" : "logger_hl", + "module path" : "logger_hl.dll", + "args" : + { + "filename":"deviceCloudUploadGatewaylog.txt"}" + } + } + ] +}``` + + + +**SRS_LOGGER_HL_02_001: [** If `busHandle` is NULL then `Logger_HL_Create` shall fail and return NULL. **]** +**SRS_LOGGER_HL_02_003: [** If `configuration` is NULL then `Logger_HL_Create` shall fail and return NULL. **]** +**SRS_LOGGER_HL_02_011: [** If configuration is not a JSON object, then `Logger_HL_Create` shall fail and return NULL. **]** +**SRS_LOGGER_HL_02_012: [** If the JSON object does not contain a value named "filename" then `Logger_HL_Create` shall fail and return NULL. **]** +**SRS_LOGGER_HL_02_005: [** `Logger_HL_Create` shall pass `busHandle` and the filename to `Logger_Create`. **]** +**SRS_LOGGER_HL_02_006: [** If `Logger_Create` succeeds then `Logger_HL_Create` shall succeed and return a non-NULL value. **]** +**SRS_LOGGER_HL_02_007: [** If `Logger_Create` fails then `Logger_HL_Create` shall fail and return NULL. **]** + +###Logger_HL_Receive +```c +void Logger_HL_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); +``` +**SRS_LOGGER_HL_02_008: [** `Logger_HL_Receive` shall pass the received parameters to the underlying Logger's `_Receive` function. **]** + + +###Logger_HL_Destroy +```c +void Logger_HL_Destroy(MODULE_HANDLE moduleHandle); +``` +**SRS_LOGGER_HL_02_009: [** `Logger_HL_Destroy` shall destroy all used resources. **]** + +###Module_GetAPIs +```c +extern const MODULE_APIS* Module_GetAPIS(void); +``` +**SRS_LOGGER_HL_02_010: [** `Module_GetAPIS` shall return a non-NULL pointer to a structure of type MODULE_APIS that has all fields non-NULL. **]** \ No newline at end of file diff --git a/modules/logger/inc/logger.h b/modules/logger/inc/logger.h new file mode 100644 index 00000000..7d880fac --- /dev/null +++ b/modules/logger/inc/logger.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef LOGGER_H +#define LOGGER_H + +#include "module.h" +#include "logger_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(LOGGER_MODULE)(void); + +#ifdef __cplusplus +} +#endif + + +#endif /*LOGGER_H*/ diff --git a/modules/logger/inc/logger_common.h b/modules/logger/inc/logger_common.h new file mode 100644 index 00000000..cb582b8c --- /dev/null +++ b/modules/logger/inc/logger_common.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef LOGGER_COMMON_H +#define LOGGER_COMMON_H + +/*the below data structures are used by all versions of Logger (static/dynamic vanilla/hl)*/ +typedef enum LOGGER_TYPE_TAG +{ + LOGGING_TO_FILE +}LOGGER_TYPE; + +typedef struct LOGGER_CONFIG_TAG +{ + LOGGER_TYPE selector; + union + { + struct LOGGER_CONFIG_FILE_TAG + { + const char* name; + } loggerConfigFile; + }selectee; +}LOGGER_CONFIG; /*this needs to be passed to the Module_Create function*/ + +#endif /*LOGGER_COMMON_H*/ diff --git a/modules/logger/inc/logger_hl.h b/modules/logger/inc/logger_hl.h new file mode 100644 index 00000000..88cadd33 --- /dev/null +++ b/modules/logger/inc/logger_hl.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef LOGGER_HL_H +#define LOGGER_HL_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(LOGGER_MODULE_HL)(void); + +#ifdef __cplusplus +} +#endif + +#endif /*LOGGER_HL_H*/ diff --git a/modules/logger/src/logger.c b/modules/logger/src/logger.c new file mode 100644 index 00000000..e760f827 --- /dev/null +++ b/modules/logger/src/logger.c @@ -0,0 +1,426 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include "azure_c_shared_utility/gballoc.h" + +#include "azure_c_shared_utility/gb_stdio.h" +#include "azure_c_shared_utility/gb_time.h" + +#include "logger.h" + +#include + +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "azure_c_shared_utility/base64.h" +#include "azure_c_shared_utility/map.h" +#include "azure_c_shared_utility/constmap.h" + +typedef struct LOGGER_HANDLE_DATA_TAG +{ + FILE* fout; +}LOGGER_HANDLE_DATA; + +/*this function adds a JSON object to the output*/ +/*function assumes the file already has a json array in it. and that the last character is ] (when the first json is appended) or , when subsequent jsons are appended*/ +static int addJSONString(FILE* fout, const char* jsonString) +{ + int result; + if (fseek(fout, -1, SEEK_END) != 0) /*rewind 1 character*/ /*What is this in C standard... "A binary stream need not meaningfully support fseek calls with a whence value of SEEK_END."???*/ + { + LogError("unable to fseek"); + result = __LINE__; + } + else + { + if (fprintf(fout, "%s", jsonString) < 0) + { + LogError("fprintf failed"); + result = __LINE__; + } + else + { + result = 0; + } + } + return result; +} + + + +static int LogStartStop_Print(char* destination, size_t destinationSize, bool appendStart, bool isAbsoluteStart) +{ + int result; + time_t temp = time(NULL); + if (temp == (time_t)-1) + { + LogError("unable to time(NULL)"); + result = __LINE__; + } + else + { + struct tm* t = localtime(&temp); + if (t == NULL) + { + LogError("localtime failed"); + result = __LINE__; + } + else + { + const char* format = appendStart ? + (isAbsoluteStart?"{\"time\":\"%c\",\"content\":\"Log started\"}]" : ",{\"time\":\"%c\",\"content\":\"Log started\"}]"): + ",{\"time\":\"%c\",\"content\":\"Log stopped\"}]"; + if (strftime(destination, destinationSize, format, t) == 0) + { + LogError("unable to strftime"); + result = __LINE__; + } + else + { + result = 0; + } + } + } + return result; +} + +static int append_logStartStop(FILE* fout, bool appendStart, bool isAbsoluteStart) +{ + int result; + /*Codes_SRS_LOGGER_02_017: [Logger_Create shall add the following JSON value to the existing array of JSON values in the file:]*/ + char temp[80] = { 0 }; + if (LogStartStop_Print(temp, sizeof(temp) / sizeof(temp[0]), appendStart, isAbsoluteStart) != 0 ) + { + LogError("unable to create start/stop time json string"); + result = __LINE__; + } + else + { + if (addJSONString(fout, temp) != 0) + { + LogError("internal error in addJSONString"); + result = __LINE__; + } + else + { + result = 0; /*all is fine*/ + } + } + return result; +} + +static MODULE_HANDLE Logger_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + LOGGER_HANDLE_DATA* result; + /*Codes_SRS_LOGGER_02_001: [If busHandle is NULL then Logger_Create shall fail and return NULL.]*/ + /*Codes_SRS_LOGGER_02_002: [If configuration is NULL then Logger_Create shall fail and return NULL.]*/ + if ( + (busHandle == NULL) || + (configuration == NULL) + ) + { + LogError("invalid arg busHandle=%p configuration=%p", busHandle, configuration); + result = NULL; + } + else + { + const LOGGER_CONFIG* config = configuration; + /*Codes_SRS_LOGGER_02_003: [If configuration->selector has a value different than LOGGING_TO_FILE then Logger_Create shall fail and return NULL.]*/ + if (config->selector != LOGGING_TO_FILE) + { + LogError("invalid arg config->selector=%d", config->selector); + result = NULL; + } + else + { + /*Codes_SRS_LOGGER_02_004: [If configuration->selectee.loggerConfigFile.name is NULL then Logger_Create shall fail and return NULL.]*/ + if (config->selectee.loggerConfigFile.name == NULL) + { + LogError("invalid arg config->selectee.loggerConfigFile.name=NULL"); + result = NULL; + } + else + { + /*Codes_SRS_LOGGER_02_005: [Logger_Create shall allocate memory for the below structure.]*/ + result = malloc(sizeof(LOGGER_HANDLE_DATA)); + /*Codes_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + if (result == NULL) + { + LogError("malloc failed"); + /*return as is*/ + } + else + { + /*Codes_SRS_LOGGER_02_006: [Logger_Create shall open the file configuration the filename selectee.loggerConfigFile.name in update (reading and writing) mode and assign the result of fopen to fout field. ]*/ + result->fout = fopen(config->selectee.loggerConfigFile.name, "r+b"); /*open binary file for update (reading and writing)*/ + if (result->fout == NULL) + { + /*if the file does not exist [or other error, indistinguishable here] try to create it*/ + /*Codes_SRS_LOGGER_02_020: [If the file selectee.loggerConfigFile.name does not exist, it shall be created.]*/ + result->fout = fopen(config->selectee.loggerConfigFile.name, "w+b");/*truncate to zero length or create binary file for update*/ + } + + /*Codes_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + /*Codes_SRS_LOGGER_02_021: [If creating selectee.loggerConfigFile.name fails then Logger_Create shall fail and return NULL.]*/ + if (result->fout == NULL) + { + LogError("unable to open file %s", config->selectee.loggerConfigFile.name); + free(result); + result = NULL; + } + else + { + /*Codes_SRS_LOGGER_02_018: [If the file does not contain a JSON array, then it shall create it.]*/ + if (fseek(result->fout, 0, SEEK_END) != 0) + { + /*Codes_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + LogError("unable to fseek to end of file"); + if (fclose(result->fout) != 0) + { + LogError("unable to close file %s", config->selectee.loggerConfigFile.name); + } + free(result); + result = NULL; + } + else + { + /*the verifications here are weak, content of file is not verified*/ + errno = 0; + long int fileSize; + if ((fileSize = ftell(result->fout)) == -1L) + { + /*Codes_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + LogError("unable to ftell (errno=%d)", errno); + if (fclose(result->fout) != 0) + { + LogError("unable to close file %s", config->selectee.loggerConfigFile.name); + } + free(result); + result = NULL; + } + else + { + /*Codes_SRS_LOGGER_02_018: [If the file does not contain a JSON array, then it shall create it.]*/ + if (fileSize == 0) + { + /*Codes_SRS_LOGGER_02_018: [If the file does not contain a JSON array, then it shall create it.]*/ + if (fprintf(result->fout, "[]") < 0) /*add an empty array of JSONs to the output file*/ + { + /*Codes_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + LogError("unable to write to output file"); + if (fclose(result->fout) != 0) + { + LogError("unable to close file %s", config->selectee.loggerConfigFile.name); + } + free(result); + result = NULL; + } + else + { + /*Codes_SRS_LOGGER_02_017: [Logger_Create shall add the following JSON value to the existing array of JSON values in the file:]*/ + if (append_logStartStop(result->fout, true, true) != 0) + { + LogError("append_logStartStop failed"); + if (fclose(result->fout) != 0) + { + LogError("unable to close file %s", config->selectee.loggerConfigFile.name); + } + free(result); + result = NULL; + } + /*Codes_SRS_LOGGER_02_008: [Otherwise Logger_Create shall return a non-NULL pointer.]*/ + /*that is, return as is*/ + } + } + else + { + /*Codes_SRS_LOGGER_02_017: [Logger_Create shall add the following JSON value to the existing array of JSON values in the file:]*/ + if (append_logStartStop(result->fout, true, false) != 0) + { + LogError("append_logStartStop failed"); + } + /*Codes_SRS_LOGGER_02_008: [Otherwise Logger_Create shall return a non-NULL pointer.]*/ + /*that is, return as is*/ + } + } + } + } + } + } + } + } + return result; +} + +static void Logger_Destroy(MODULE_HANDLE module) +{ + /*Codes_SRS_LOGGER_02_014: [If moduleHandle is NULL then Logger_Destroy shall return.]*/ + if (module != NULL) + { + /*Codes_SRS_LOGGER_02_019: [Logger_Destroy shall add to the log file the following end of log JSON object:]*/ + LOGGER_HANDLE_DATA* moduleHandleData = (LOGGER_HANDLE_DATA *)module; + if (append_logStartStop(moduleHandleData->fout, false, false) != 0) + { + LogError("unable to append log ending time"); + } + + /*Codes_SRS_LOGGER_02_015: [Otherwise Logger_Destroy shall unuse all used resources.]*/ + if (fclose(moduleHandleData->fout) != 0) + { + LogError("unable to fclose"); + } + + free(moduleHandleData); + + } +} + +static void Logger_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + /*Codes_SRS_LOGGER_02_009: [If moduleHandle is NULL then Logger_Receive shall fail and return.]*/ + /*Codes_SRS_LOGGER_02_010: [If messageHandle is NULL then Logger_Receive shall fail and return.]*/ + if ( + (moduleHandle == NULL) || + (messageHandle == NULL) + ) + { + LogError("invalid arg moduleHandle = %p", moduleHandle); + } + else + { + /*Codes_SRS_LOGGER_02_011: [Logger_Receive shall write in the fout FILE the following information in JSON format:]*/ + /* + { + "time":"timeAsPrinted by strftime(\"%c\")", + "properties": + { + "property1":"value1", + "property2":"value2" + }, + "content":"base64 encode of the message content" + }, + */ + + /*the function will gather first all the values then will dump them into a STRING_HANDLE that is JSON*/ + + /*getting the time*/ + time_t temp = time(NULL); + if (temp == (time_t)-1) + { + LogError("time function failed"); + } + else + { + struct tm* t = localtime(&temp); + if (t == NULL) + { + LogError("localtime failed"); + } + else + { + char timetemp[80] = { 0 }; + if (strftime(timetemp, sizeof(timetemp) / sizeof(timetemp[0]), "%c", t) == 0) + { + LogError("unable to strftime"); + /*Codes_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + /*just return*/ + } + else + { + /*getting the properties*/ + /*getting the constmap*/ + CONSTMAP_HANDLE originalProperties = Message_GetProperties(messageHandle); /*by contract this is never NULL*/ + MAP_HANDLE propertiesAsMap = ConstMap_CloneWriteable(originalProperties); /*sigh, if only there'd be a constmap_tojson*/ + if (propertiesAsMap == NULL) + { + LogError("ConstMap_CloneWriteable failed"); + } + else + { + STRING_HANDLE jsonProperties = Map_ToJSON(propertiesAsMap); + if (jsonProperties == NULL) + { + LogError("unable to Map_ToJSON"); + } + else + { + /*getting the base64 encode of the message*/ + const CONSTBUFFER * content = Message_GetContent(messageHandle); /*by contract, this is never NULL*/ + STRING_HANDLE contentAsJSON = Base64_Encode_Bytes(content->buffer, content->size); + if (contentAsJSON == NULL) + { + LogError("unable to Base64_Encode_Bytes"); + } + else + { + STRING_HANDLE jsonToBeAppended = STRING_construct(",{\"time\":\""); + if (jsonToBeAppended == NULL) + { + LogError("unable to STRING_construct"); + } + else + { + + if (!( + (STRING_concat(jsonToBeAppended, timetemp) == 0) && + (STRING_concat(jsonToBeAppended, "\",\"properties\":") == 0) && + (STRING_concat_with_STRING(jsonToBeAppended, jsonProperties) == 0) && + (STRING_concat(jsonToBeAppended, ",\"content\":\"") == 0) && + (STRING_concat_with_STRING(jsonToBeAppended, contentAsJSON) == 0) && + (STRING_concat(jsonToBeAppended, "\"}]") == 0) + )) + { + LogError("STRING concatenation error"); + } + else + { + LOGGER_HANDLE_DATA *handleData = (LOGGER_HANDLE_DATA *)moduleHandle; + if (addJSONString(handleData->fout, STRING_c_str(jsonToBeAppended)) != 0) + { + LogError("failed top add a json string to the output file"); + } + else + { + /*all seems fine*/ + } + } + STRING_delete(jsonToBeAppended); + } + STRING_delete(contentAsJSON); + } + STRING_delete(jsonProperties); + } + Map_Destroy(propertiesAsMap); + } + ConstMap_Destroy(originalProperties); + } + } + } + } + /*Codes_SRS_LOGGER_02_013: [Logger_Receive shall return.]*/ +} + +/* + * Required for all modules: the public API and the designated implementation functions. + */ +static const MODULE_APIS Logger_APIS_all = +{ + Logger_Create, + Logger_Destroy, + Logger_Receive +}; + +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(LOGGER_MODULE)(void) +#else +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + /*Codes_SRS_LOGGER_02_016: [Module_GetAPIS shall return a non-NULL pointer to a structure of type MODULE_APIS that has all fields non-NULL.]*/ + return &Logger_APIS_all; +} diff --git a/modules/logger/src/logger_hl.c b/modules/logger/src/logger_hl.c new file mode 100644 index 00000000..227e4c8b --- /dev/null +++ b/modules/logger/src/logger_hl.c @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include "azure_c_shared_utility/gballoc.h" + +/*because it is linked statically, this include will bring in some uniquely (by convention) named functions*/ +#include "logger.h" + +#include "logger_hl.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "parson.h" + +static MODULE_HANDLE Logger_HL_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + + MODULE_HANDLE result; + /*Codes_SRS_LOGGER_HL_02_001: [ If busHandle is NULL then Logger_HL_Create shall fail and return NULL. ]*/ + /*Codes_SRS_LOGGER_HL_02_003: [ If configuration is NULL then Logger_HL_Create shall fail and return NULL. ]*/ + if( + (busHandle == NULL) || + (configuration == NULL) + ) + { + LogError("NULL parameter detected busHandle=%p configuration=%p", busHandle, configuration); + result = NULL; + } + else + { + /*Codes_SRS_LOGGER_HL_02_011: [ If configuration is not a JSON object, then Logger_HL_Create shall fail and return NULL. ]*/ + JSON_Value* json = json_parse_string((const char*)configuration); + if (json == NULL) + { + LogError("unable to json_parse_string"); + result = NULL; + } + else + { + JSON_Object* obj = json_value_get_object(json); + if (obj == NULL) + { + LogError("unable to json_value_get_object"); + result = NULL; + } + else + { + /*Codes_SRS_LOGGER_HL_02_012: [ If the JSON object does not contain a value named "filename" then Logger_HL_Create shall fail and return NULL. ]*/ + const char* fileNameValue = json_object_get_string(obj, "filename"); + if (fileNameValue == NULL) + { + LogError("json_object_get_string failed"); + result = NULL; + } + else + { + /*fileNameValue is believed at this moment to be a string that might point to a filename on the system*/ + + LOGGER_CONFIG config; + config.selector = LOGGING_TO_FILE; + config.selectee.loggerConfigFile.name = fileNameValue; + + /*Codes_SRS_LOGGER_HL_02_005: [ Logger_HL_Create shall pass busHandle and the filename to Logger_Create. ]*/ + result = MODULE_STATIC_GETAPIS(LOGGER_MODULE)()->Module_Create(busHandle, &config); + + if (result == NULL) + { + /*Codes_SRS_LOGGER_HL_02_007: [ If Logger_Create fails then Logger_HL_Create shall fail and return NULL. ]*/ + /*return result "as is" - that is - NULL*/ + LogError("unable to create Logger"); + } + else + { + /*Codes_SRS_LOGGER_HL_02_006: [ If Logger_Create succeeds then Logger_HL_Create shall succeed and return a non-NULL value. ]*/ + /*return result "as is" - that is - not NULL*/ + } + } + } + json_value_free(json); + } + } + + return result; +} + +static void Logger_HL_Destroy(MODULE_HANDLE module) +{ + /*Codes_SRS_LOGGER_HL_02_009: [ Logger_HL_Destroy shall destroy all used resources. ]*/ /*in this case "all" is "none"*/ + MODULE_STATIC_GETAPIS(LOGGER_MODULE)()->Module_Destroy(module); +} + +static void Logger_HL_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + /*Codes_SRS_LOGGER_HL_02_008: [ Logger_HL_Receive shall pass the received parameters to the underlying Logger's _Receive function. ]*/ + MODULE_STATIC_GETAPIS(LOGGER_MODULE)()->Module_Receive(moduleHandle, messageHandle); +} + +/* + * Required for all modules: the public API and the designated implementation functions. + */ +static const MODULE_APIS Logger_HL_APIS_all = +{ + Logger_HL_Create, + Logger_HL_Destroy, + Logger_HL_Receive +}; + +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(LOGGER_MODULE_HL)(void) +#else +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + /*Codes_SRS_LOGGER_HL_02_010: [ Module_GetAPIS shall return a non-NULL pointer to a structure of type MODULE_APIS that has all fields non-NULL. ]*/ + return &Logger_HL_APIS_all; +} diff --git a/modules/logger/tests/CMakeLists.txt b/modules/logger/tests/CMakeLists.txt new file mode 100644 index 00000000..eaa84d94 --- /dev/null +++ b/modules/logger/tests/CMakeLists.txt @@ -0,0 +1,8 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) +#this is CMakeLists for logger tests folder + +add_subdirectory(logger_unittests) +add_subdirectory(logger_hl_unittests) diff --git a/modules/logger/tests/logger_hl_unittests/CMakeLists.txt b/modules/logger/tests/logger_hl_unittests/CMakeLists.txt new file mode 100644 index 00000000..a5082fa6 --- /dev/null +++ b/modules/logger/tests/logger_hl_unittests/CMakeLists.txt @@ -0,0 +1,22 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for logger_hl_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName logger_hl_unittests) +set(${theseTestsName}_cpp_files +${theseTestsName}.cpp +) + +set(${theseTestsName}_c_files + ../../src/logger_hl.c +) + +set(${theseTestsName}_h_files +) + +include_directories(${GW_INC} ../../inc) + +build_test_artifacts(${theseTestsName} ON) diff --git a/modules/logger/tests/logger_hl_unittests/logger_hl_unittests.cpp b/modules/logger/tests/logger_hl_unittests/logger_hl_unittests.cpp new file mode 100644 index 00000000..7fb580f1 --- /dev/null +++ b/modules/logger/tests/logger_hl_unittests/logger_hl_unittests.cpp @@ -0,0 +1,738 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" +#include "azure_c_shared_utility/lock.h" + +#include "logger.h" +#include "logger_hl.h" + +#define GBALLOC_H +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit + +#include /* size_t */ + + /* Types and enums */ + typedef struct json_object_t JSON_Object; + typedef struct json_array_t JSON_Array; + typedef struct json_value_t JSON_Value; + + enum json_value_type { + JSONError = -1, + JSONNull = 1, + JSONString = 2, + JSONNumber = 3, + JSONObject = 4, + JSONArray = 5, + JSONBoolean = 6 + }; + typedef int JSON_Value_Type; + + enum json_result_t { + JSONSuccess = 0, + JSONFailure = -1 + }; + typedef int JSON_Status; + + typedef void * (*JSON_Malloc_Function)(size_t); + typedef void(*JSON_Free_Function)(void *); + + /* Call only once, before calling any other function from parson API. If not called, malloc and free + from stdlib will be used for all allocations */ + void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun); + + /* Parses first JSON value in a file, returns NULL in case of error */ + JSON_Value * json_parse_file(const char *filename); + + /* Parses first JSON value in a file and ignores comments (/ * * / and //), + returns NULL in case of error */ + JSON_Value * json_parse_file_with_comments(const char *filename); + + /* Parses first JSON value in a string, returns NULL in case of error */ + JSON_Value * json_parse_string(const char *string); + + /* Parses first JSON value in a string and ignores comments (/ * * / and //), + returns NULL in case of error */ + JSON_Value * json_parse_string_with_comments(const char *string); + + /* Serialization */ + size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */ + JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); + JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename); + char * json_serialize_to_string(const JSON_Value *value); + + /* Pretty serialization */ + size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */ + JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); + JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename); + char * json_serialize_to_string_pretty(const JSON_Value *value); + + void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */ + + /* Comparing */ + int json_value_equals(const JSON_Value *a, const JSON_Value *b); + + /* Validation + This is *NOT* JSON Schema. It validates json by checking if object have identically + named fields with matching types. + For example schema {"name":"", "age":0} will validate + {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"}, + but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}. + In case of arrays, only first value in schema is checked against all values in tested array. + Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays, + null validates values of every type. + */ + JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value); + + /* + * JSON Object + */ + JSON_Value * json_object_get_value(const JSON_Object *object, const char *name); + const char * json_object_get_string(const JSON_Object *object, const char *name); + JSON_Object * json_object_get_object(const JSON_Object *object, const char *name); + JSON_Array * json_object_get_array(const JSON_Object *object, const char *name); + double json_object_get_number(const JSON_Object *object, const char *name); /* returns 0 on fail */ + int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + + /* dotget functions enable addressing values with dot notation in nested objects, + just like in structs or c++/java/c# objects (e.g. objectA.objectB.value). + Because valid names in JSON can contain dots, some values may be inaccessible + this way. */ + JSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name); + const char * json_object_dotget_string(const JSON_Object *object, const char *name); + JSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name); + JSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name); + double json_object_dotget_number(const JSON_Object *object, const char *name); /* returns 0 on fail */ + int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + + /* Functions to get available names */ + size_t json_object_get_count(const JSON_Object *object); + const char * json_object_get_name(const JSON_Object *object, size_t index); + + /* Creates new name-value pair or frees and replaces old value with a new one. + * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */ + JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value); + JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string); + JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number); + JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean); + JSON_Status json_object_set_null(JSON_Object *object, const char *name); + + /* Works like dotget functions, but creates whole hierarchy if necessary. + * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */ + JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value); + JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string); + JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number); + JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean); + JSON_Status json_object_dotset_null(JSON_Object *object, const char *name); + + /* Frees and removes name-value pair */ + JSON_Status json_object_remove(JSON_Object *object, const char *name); + + /* Works like dotget function, but removes name-value pair only on exact match. */ + JSON_Status json_object_dotremove(JSON_Object *object, const char *key); + + /* Removes all name-value pairs in object */ + JSON_Status json_object_clear(JSON_Object *object); + + /* + *JSON Array + */ + JSON_Value * json_array_get_value(const JSON_Array *array, size_t index); + const char * json_array_get_string(const JSON_Array *array, size_t index); + JSON_Object * json_array_get_object(const JSON_Array *array, size_t index); + JSON_Array * json_array_get_array(const JSON_Array *array, size_t index); + double json_array_get_number(const JSON_Array *array, size_t index); /* returns 0 on fail */ + int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */ + size_t json_array_get_count(const JSON_Array *array); + + /* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist. + * Order of values in array may change during execution. */ + JSON_Status json_array_remove(JSON_Array *array, size_t i); + + /* Frees and removes from array value at given index and replaces it with given one. + * Does nothing and returns JSONFailure if index doesn't exist. + * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */ + JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value); + JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string); + JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number); + JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean); + JSON_Status json_array_replace_null(JSON_Array *array, size_t i); + + /* Frees and removes all values from array */ + JSON_Status json_array_clear(JSON_Array *array); + + /* Appends new value at the end of array. + * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */ + JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value); + JSON_Status json_array_append_string(JSON_Array *array, const char *string); + JSON_Status json_array_append_number(JSON_Array *array, double number); + JSON_Status json_array_append_boolean(JSON_Array *array, int boolean); + JSON_Status json_array_append_null(JSON_Array *array); + + /* + *JSON Value + */ + JSON_Value * json_value_init_object(void); + JSON_Value * json_value_init_array(void); + JSON_Value * json_value_init_string(const char *string); /* copies passed string */ + JSON_Value * json_value_init_number(double number); + JSON_Value * json_value_init_boolean(int boolean); + JSON_Value * json_value_init_null(void); + JSON_Value * json_value_deep_copy(const JSON_Value *value); + void json_value_free(JSON_Value *value); + + JSON_Value_Type json_value_get_type(const JSON_Value *value); + JSON_Object * json_value_get_object(const JSON_Value *value); + JSON_Array * json_value_get_array(const JSON_Value *value); + const char * json_value_get_string(const JSON_Value *value); + double json_value_get_number(const JSON_Value *value); + int json_value_get_boolean(const JSON_Value *value); + + /* Same as above, but shorter */ + JSON_Value_Type json_type(const JSON_Value *value); + JSON_Object * json_object(const JSON_Value *value); + JSON_Array * json_array(const JSON_Value *value); + const char * json_string(const JSON_Value *value); + double json_number(const JSON_Value *value); + int json_boolean(const JSON_Value *value); + +#define parson_parson_h +#ifdef _CRT_SECURE_NO_WARNINGS +#undef _CRT_SECURE_NO_WARNINGS +#include "parson.c" +#define _CRT_SECURE_NO_WARNINGS +#else +#include "parson.c" +#endif +}; + +#undef parson_parson_h +#include "parson.h" + +/*forward declarations*/ +MODULE_HANDLE Logger_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +/*this destroys (frees resources) of the module parameter*/ +void Logger_Destroy(MODULE_HANDLE moduleHandle); +/*this is the module's callback function - gets called when a message is to be received by the module*/ +void Logger_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); + +static MODULE_APIS Logger_APIS = +{ + Logger_Create, + Logger_Destroy, + Logger_Receive +}; + +TYPED_MOCK_CLASS(CLoggerMocks, CGlobalMock) +{ +public: + + /*Parson Mocks*/ + MOCK_STATIC_METHOD_1(, JSON_Value*, json_parse_string, const char *, filename) + JSON_Value* value = NULL; + if (filename != NULL) + { + value = (JSON_Value*)malloc(sizeof(BASEIMPLEMENTATION::JSON_Value)); + } + MOCK_METHOD_END(JSON_Value*, value); + + MOCK_STATIC_METHOD_1(, JSON_Value*, json_parse_file, const char *, filename) + JSON_Value* value = NULL; + if (filename != NULL) + { + value = (JSON_Value*)malloc(sizeof(BASEIMPLEMENTATION::JSON_Value)); + } + MOCK_METHOD_END(JSON_Value*, value); + + MOCK_STATIC_METHOD_1(, JSON_Object*, json_value_get_object, const JSON_Value*, value) + JSON_Object* object = NULL; + if (value != NULL) + { + object = (JSON_Object*)0x42; + } + MOCK_METHOD_END(JSON_Object*, object); + + MOCK_STATIC_METHOD_2(, JSON_Array*, json_object_get_array, const JSON_Object*, object, const char*, name) + JSON_Array* arr = NULL; + if (object != NULL && name != NULL) + { + arr = (JSON_Array*)0x42; + } + MOCK_METHOD_END(JSON_Array*, arr); + + MOCK_STATIC_METHOD_1(, size_t, json_array_get_count, const JSON_Array*, arr) + size_t size = 0; + MOCK_METHOD_END(size_t, size); + + MOCK_STATIC_METHOD_2(, JSON_Object*, json_array_get_object, const JSON_Array*, arr, size_t, index) + JSON_Object* object = NULL; + if (arr != NULL && index >= 0) + { + object = (JSON_Object*)0x42; + } + MOCK_METHOD_END(JSON_Object*, object); + + MOCK_STATIC_METHOD_2(, const char*, json_object_get_string, const JSON_Object*, object, const char*, name) + MOCK_METHOD_END(const char*, (strcmp(name, "filename")==0)?"log.txt":NULL); + + MOCK_STATIC_METHOD_2(, JSON_Value*, json_object_get_value, const JSON_Object*, object, const char*, name) + JSON_Value* value = NULL; + if (object != NULL && name != NULL) + { + value = (JSON_Value*)0x42; + } + MOCK_METHOD_END(JSON_Value*, value); + + MOCK_STATIC_METHOD_1(, char*, json_serialize_to_string, const JSON_Value*, value) + char* serialized_string = NULL; + const char* text = "[serialized string]"; + if (value != NULL) + { + serialized_string = (char*)malloc(sizeof(char) * strlen(text) + 1); + strcpy(serialized_string, text); + } + MOCK_METHOD_END(char*, serialized_string); + + MOCK_STATIC_METHOD_1(, void, json_value_free, JSON_Value*, value) + free(value); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_1(, void, json_free_serialized_string, char*, string) + free(string); + MOCK_VOID_METHOD_END(); + + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, STRING_HANDLE, STRING_construct, const char*, source) + STRING_HANDLE result2 = (STRING_HANDLE)malloc(4); + MOCK_METHOD_END(STRING_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, STRING_delete, STRING_HANDLE, s) + free(s); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, int, STRING_concat, STRING_HANDLE, s1, const char*, s2) + MOCK_METHOD_END(int, 0); + + MOCK_STATIC_METHOD_2(, int, STRING_concat_with_STRING, STRING_HANDLE, s1, STRING_HANDLE, s2) + MOCK_METHOD_END(int, 0); + + MOCK_STATIC_METHOD_1(, const char*, STRING_c_str, STRING_HANDLE, s) + MOCK_METHOD_END(const char*, "thisIsRandomContent") + + MOCK_STATIC_METHOD_1(, MAP_HANDLE, ConstMap_CloneWriteable, CONSTMAP_HANDLE, handle) + MAP_HANDLE result2 = (MAP_HANDLE)malloc(5); + MOCK_METHOD_END(MAP_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, Map_Destroy, MAP_HANDLE, ptr) + free(ptr); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, STRING_HANDLE, Map_ToJSON, MAP_HANDLE, handle) + STRING_HANDLE result2 = (STRING_HANDLE)malloc(6); + MOCK_METHOD_END(STRING_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message) + CONSTMAP_HANDLE result2 = (CONSTMAP_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(1); + MOCK_METHOD_END(CONSTMAP_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, ConstMap_Destroy, CONSTMAP_HANDLE, handle) + free(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, STRING_HANDLE, Base64_Encode_Bytes, const unsigned char*, source, size_t, size); + STRING_HANDLE result2 = (STRING_HANDLE)malloc(7); + MOCK_METHOD_END(STRING_HANDLE, result2); + + MOCK_STATIC_METHOD_2(, FILE*, gb_fopen, const char*, filename, const char*, mode) + FILE* result2 = (FILE*)malloc(8); + MOCK_METHOD_END(FILE*, result2); + + MOCK_STATIC_METHOD_1(, int, gb_fclose, FILE*, file) + free(file); + int result2 = 0; + MOCK_METHOD_END(int, result2); + + MOCK_STATIC_METHOD_3(, int, gb_fseek, FILE *, stream, long int, offset, int, whence) + int result2 = 0; + MOCK_METHOD_END(int, result2); + + MOCK_STATIC_METHOD_1(, long int, gb_ftell, FILE *, stream) + long int result2 = 0; + MOCK_METHOD_END(long int, result2); + + MOCK_STATIC_METHOD_1(, time_t, gb_time, time_t *, timer) + time_t result2 = (time_t)1; /*assume "1" is valid time_t*/ + MOCK_METHOD_END(time_t, result2); + + MOCK_STATIC_METHOD_1(, struct tm*, gb_localtime, const time_t *, timer) + struct tm* result2 = (struct tm*)0x42; + MOCK_METHOD_END(struct tm*, result2); + + MOCK_STATIC_METHOD_0(, const MODULE_APIS*, MODULE_STATIC_GETAPIS(LOGGER_MODULE)) + MOCK_METHOD_END(const MODULE_APIS*, (const MODULE_APIS*)&Logger_APIS); + + MOCK_STATIC_METHOD_2( , MODULE_HANDLE, Logger_Create, MESSAGE_BUS_HANDLE, busHandle, const void*, configuration) + MOCK_METHOD_END(MODULE_HANDLE, malloc(1)); + + MOCK_STATIC_METHOD_1(, void, Logger_Destroy, MODULE_HANDLE, moduleHandle); + { + free(moduleHandle); + } + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, void, Logger_Receive, MODULE_HANDLE, moduleHandle, MESSAGE_HANDLE, messageHandle) + MOCK_VOID_METHOD_END() +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , JSON_Value*, json_parse_file, const char *, filename); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , JSON_Value*, json_parse_string, const char *, filename); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , JSON_Object*, json_value_get_object, const JSON_Value*, value); +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , JSON_Array*, json_object_get_array, const JSON_Object*, object, const char*, name); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , size_t, json_array_get_count, const JSON_Array*, arr); +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , JSON_Object*, json_array_get_object, const JSON_Array*, arr, size_t, index); +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , const char*, json_object_get_string, const JSON_Object*, object, const char*, name); +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , JSON_Value*, json_object_get_value, const JSON_Object*, object, const char*, name); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , char*, json_serialize_to_string, const JSON_Value*, value); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void, json_value_free, JSON_Value*, value); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void, json_free_serialized_string, char*, string); + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void, gballoc_free, void*, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , STRING_HANDLE, STRING_construct, const char*, s); + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void, STRING_delete, STRING_HANDLE, s); +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , int, STRING_concat, STRING_HANDLE, s1, const char*, s2); +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , int, STRING_concat_with_STRING, STRING_HANDLE, s1, STRING_HANDLE, s2); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , const char*, STRING_c_str, STRING_HANDLE, s); + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , MAP_HANDLE, ConstMap_CloneWriteable, CONSTMAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , STRING_HANDLE, Map_ToJSON, MAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void, Map_Destroy, MAP_HANDLE, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void, ConstMap_Destroy, CONSTMAP_HANDLE, handle); + +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , STRING_HANDLE, Base64_Encode_Bytes, const unsigned char*, source, size_t, size); + +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , FILE*, gb_fopen, const char*, filename, const char*, mode); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , int, gb_fclose, FILE*, stream); +DECLARE_GLOBAL_MOCK_METHOD_3(CLoggerMocks, , int, gb_fseek, FILE *, stream, long int, offset, int, whence) +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , long int, gb_ftell, FILE*, stream) + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , time_t, gb_time, time_t*, timer); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , struct tm*, gb_localtime, const time_t*, timer); + +DECLARE_GLOBAL_MOCK_METHOD_0(CLoggerMocks, , const MODULE_APIS*, MODULE_STATIC_GETAPIS(LOGGER_MODULE)); + +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , MODULE_HANDLE, Logger_Create, MESSAGE_BUS_HANDLE, busHandle, const void*, configuration); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void, Logger_Destroy, MODULE_HANDLE, moduleHandle); +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , void, Logger_Receive, MODULE_HANDLE, moduleHandle, MESSAGE_HANDLE, messageHandle); + +/*definitions of cached functions, initialized in TEST_FIUCNTION_INIT*/ + +MODULE_HANDLE (*Logger_HL_Create)(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +/*this destroys (frees resources) of the module parameter*/ +void (*Logger_HL_Destroy)(MODULE_HANDLE moduleHandle); +/*this is the module's callback function - gets called when a message is to be received by the module*/ +void (*Logger_HL_Receive)(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); + +static MESSAGE_BUS_HANDLE validBusHandle = (MESSAGE_BUS_HANDLE)0x1; +static MESSAGE_HANDLE VALID_MESSAGE_HANDLE = (MESSAGE_HANDLE)0x02; +#define VALID_CONFIG_STRING "{\"filename\":\"log.txt\"}" + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; +BEGIN_TEST_SUITE(logger_hl_unittests) + + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + + Logger_HL_Create = Module_GetAPIS()->Module_Create; + Logger_HL_Destroy = Module_GetAPIS()->Module_Destroy; + Logger_HL_Receive = Module_GetAPIS()->Module_Receive; + + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + } + + /*Tests_SRS_LOGGER_HL_02_001: [ If busHandle is NULL then Logger_HL_Create shall fail and return NULL. ]*/ + TEST_FUNCTION(Logger_HL_Create_with_NULL_busHandle_fails) + { + ///arrage + CLoggerMocks mocks; + + ///act + auto result = Logger_HL_Create(NULL, "someConfig"); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_HL_02_003: [ If configuration is NULL then Logger_HL_Create shall fail and return NULL. ]*/ + TEST_FUNCTION(Logger_HL_Create_with_NULL_configuration_fails) + { + ///arrage + CLoggerMocks mocks; + + ///act + auto result = Logger_HL_Create(validBusHandle, NULL); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_HL_02_005: [ Logger_HL_Create shall pass busHandle and the filename to Logger_Create. ]*/ + /*Tests_SRS_LOGGER_HL_02_006: [ If Logger_Create succeeds then Logger_HL_Create shall succeed and return a non-NULL value. ]*/ + TEST_FUNCTION(Logger_HL_Create_happy_path_succeeds) + { + ///arrage + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(VALID_CONFIG_STRING)); /*this is creating the JSON from the string*/ + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) /*this is destroy of the json value created from the string*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) /*getting the json object out of the json value*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "filename")) /*this is getting a json string that is what follows "filename": in the json*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, C1(MODULE_STATIC_GETAPIS(LOGGER_MODULE)())); /*this is finding the Logger's API*/ + STRICT_EXPECTED_CALL(mocks, Logger_Create(validBusHandle, IGNORED_PTR_ARG)) /*this is calling Logger_Create function*/ + .IgnoreArgument(2); + + ///act + auto result = Logger_HL_Create(validBusHandle, VALID_CONFIG_STRING); + + ///assert + ASSERT_IS_NOT_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Logger_HL_Destroy(result); + } + + /*Tests_SRS_LOGGER_HL_02_007: [ If Logger_Create fails then Logger_HL_Create shall fail and return NULL. ]*/ + TEST_FUNCTION(Logger_HL_Create_fails_when_Logger_Create_fails) + { + ///arrage + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(VALID_CONFIG_STRING)); /*this is creating the JSON from the string*/ + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) /*this is destroy of the json value created from the string*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) /*getting the json object out of the json value*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "filename")) /*this is getting a json string that is what follows "filename": in the json*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(LOGGER_MODULE)()); /*this is finding the Logger's API*/ + STRICT_EXPECTED_CALL(mocks, Logger_Create(validBusHandle, IGNORED_PTR_ARG)) /*this is calling Logger_Create function*/ + .IgnoreArgument(2) + .SetFailReturn((MODULE_HANDLE)NULL); + + ///act + auto result = Logger_HL_Create(validBusHandle, VALID_CONFIG_STRING); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_HL_02_012: [ If the JSON object does not contain a value named "filename" then Logger_HL_Create shall fail and return NULL. ]*/ + TEST_FUNCTION(Logger_HL_Create_fails_when_json_object_get_string_fails) + { + ///arrage + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(VALID_CONFIG_STRING)); /*this is creating the JSON from the string*/ + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) /*this is destroy of the json value created from the string*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) /*getting the json object out of the json value*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_object_get_string(IGNORED_PTR_ARG, "filename")) /*this is getting a json string that is what follows "filename": in the json*/ + .IgnoreArgument(1) + .SetFailReturn((const char*)NULL); + + ///act + auto result = Logger_HL_Create(validBusHandle, VALID_CONFIG_STRING); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_HL_02_012: [ If the JSON object does not contain a value named "filename" then Logger_HL_Create shall fail and return NULL. ]*/ + TEST_FUNCTION(Logger_HL_Create_fails_when_json_value_get_object_fails) + { + ///arrage + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(VALID_CONFIG_STRING)); /*this is creating the JSON from the string*/ + STRICT_EXPECTED_CALL(mocks, json_value_free(IGNORED_PTR_ARG)) /*this is destroy of the json value created from the string*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, json_value_get_object(IGNORED_PTR_ARG)) /*getting the json object out of the json value*/ + .IgnoreArgument(1) + .SetFailReturn((JSON_Object*)NULL); + + ///act + auto result = Logger_HL_Create(validBusHandle, VALID_CONFIG_STRING); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_HL_02_011: [ If configuration is not a JSON object, then Logger_HL_Create shall fail and return NULL. ]*/ + TEST_FUNCTION(Logger_HL_Create_fails_when_json_parse_string_fails) + { + ///arrage + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, json_parse_string(VALID_CONFIG_STRING)) /*this is creating the JSON from the string*/ + .SetFailReturn((JSON_Value*)NULL); + + ///act + auto result = Logger_HL_Create(validBusHandle, VALID_CONFIG_STRING); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_HL_02_008: [ Logger_HL_Receive shall pass the received parameters to the underlying Logger's _Receive function. ]*/ + TEST_FUNCTION(Logger_HL_Receive_passthrough_succeeds) + { + ///arrage + CLoggerMocks mocks; + MODULE_HANDLE handle = Logger_HL_Create(validBusHandle, VALID_CONFIG_STRING); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(LOGGER_MODULE)()); /*this is finding the Logger's API*/ + STRICT_EXPECTED_CALL(mocks, Logger_Receive(handle, VALID_MESSAGE_HANDLE)); /*this is calling Logger_Receive function*/ + + ///act + Logger_HL_Receive(handle, VALID_MESSAGE_HANDLE); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Logger_HL_Destroy(handle); + } + + /*Tests_SRS_LOGGER_HL_02_009: [ Logger_HL_Destroy shall destroy all used resources. ]*/ + TEST_FUNCTION(Logger_HL_Destroy_passthrough_succeeds) + { + ///arrage + CLoggerMocks mocks; + MODULE_HANDLE handle = Logger_HL_Create(validBusHandle, VALID_CONFIG_STRING); + mocks.ResetAllCalls(); + + STRICT_EXPECTED_CALL(mocks, MODULE_STATIC_GETAPIS(LOGGER_MODULE)()); /*this is finding the Logger's API*/ + STRICT_EXPECTED_CALL(mocks, Logger_Destroy(handle)); /*this is calling Logger_Receive function*/ + + ///act + Logger_HL_Destroy(handle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_HL_02_010: [ Module_GetAPIS shall return a non-NULL pointer to a structure of type MODULE_APIS that has all fields non-NULL. ]*/ + TEST_FUNCTION(Module_GetAPIS_returns_non_NULL) + { + ///arrage + CLoggerMocks mocks; + + ///act + const MODULE_APIS* apis = Module_GetAPIS(); + + ///assert + ASSERT_IS_NOT_NULL(apis); + ASSERT_IS_NOT_NULL(apis->Module_Destroy); + ASSERT_IS_NOT_NULL(apis->Module_Create); + ASSERT_IS_NOT_NULL(apis->Module_Receive); + + ///cleanup + } + +END_TEST_SUITE(logger_hl_unittests) diff --git a/modules/logger/tests/logger_hl_unittests/main.c b/modules/logger/tests/logger_hl_unittests/main.c new file mode 100644 index 00000000..895dae73 --- /dev/null +++ b/modules/logger/tests/logger_hl_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include "testrunnerswitcher.h" +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(logger_hl_unittests, failedTestCount); + return failedTestCount; +} diff --git a/modules/logger/tests/logger_unittests/CMakeLists.txt b/modules/logger/tests/logger_unittests/CMakeLists.txt new file mode 100644 index 00000000..915ee5f2 --- /dev/null +++ b/modules/logger/tests/logger_unittests/CMakeLists.txt @@ -0,0 +1,24 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for logger_unittests +cmake_minimum_required(VERSION 2.8.11) + +compileAsC99() +set(theseTestsName logger_unittests) +set(${theseTestsName}_cpp_files +${theseTestsName}.cpp +) + +set(${theseTestsName}_c_files + ../../src/logger.c +) + +set(${theseTestsName}_h_files +) + +include_directories(${GW_INC} ../../inc) + +add_definitions(-DGB_STDIO_INTERCEPT -DGB_TIME_INTERCEPT -DNO_LOGGING) + +build_test_artifacts(${theseTestsName} ON) diff --git a/modules/logger/tests/logger_unittests/logger_unittests.cpp b/modules/logger/tests/logger_unittests/logger_unittests.cpp new file mode 100644 index 00000000..25d77b21 --- /dev/null +++ b/modules/logger/tests/logger_unittests/logger_unittests.cpp @@ -0,0 +1,1939 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "testrunnerswitcher.h" +#include "micromock.h" +#include "micromockcharstararenullterminatedstrings.h" + +#include +/*general macros useful for MOCKS*/ +#define CURRENT_API_CALL(API) C2(C2(current, API), _call) +#define WHEN_SHALL_API_FAIL(API) C2(C2(whenShall, API), _fail) +#define DEFINE_FAIL_VARIABLES(API) static size_t CURRENT_API_CALL(API); static size_t WHEN_SHALL_API_FAIL(API); +#define MAKE_FAIL(API, WHEN) do{WHEN_SHALL_API_FAIL(API) = CURRENT_API_CALL(API) + WHEN;} while(0) +#define RESET_API_COUNTERS(API) CURRENT_API_CALL(API) = WHEN_SHALL_API_FAIL(API) = 0; + +#define LIST_OF_COUNTED_APIS \ +gb_fprintf \ + + +FOR_EACH_1(DEFINE_FAIL_VARIABLES, LIST_OF_COUNTED_APIS) + +#include "azure_c_shared_utility/lock.h" +#include "module.h" +#include "azure_c_shared_utility/strings.h" +#include "azure_c_shared_utility/constmap.h" +#include "azure_c_shared_utility/map.h" +#include "message.h" +#include "azure_c_shared_utility/base64.h" +#include "logger.h" + +#ifndef GB_STDIO_INTERCEPT +#error these unit tests require the symbol GB_STDIO_INTERCEPT to be defined +#else +extern "C" +{ + extern FILE* gb_fopen(const char* fileName, const char* mode); + extern int gb_fclose(FILE *stream); + extern int gb_fseek(FILE *stream, long int offset, int whence); + extern long int gb_ftell(FILE *stream); + extern int gb_fprintf(FILE * stream, const char * format, ...); /*this needs poor man mocks because of ...*/ +} +#endif + +static char all_fprintfs[100][10000]; +/*poor man mock*/ +int gb_fprintf(FILE * stream, const char * format, ...) /*cannot be static*/ +{ + CURRENT_API_CALL(gb_fprintf)++; + + va_list args; + va_start(args, format); + (void)vsnprintf(all_fprintfs[CURRENT_API_CALL(gb_fprintf)-1], sizeof(all_fprintfs[0]) / sizeof(all_fprintfs[0][0]), format, args); /*no test here requires this to be 10000... */ + va_end(args); + + int result2 = (WHEN_SHALL_API_FAIL(gb_fprintf) == CURRENT_API_CALL(gb_fprintf)) ? -1 : __LINE__; + + return result2; +} + + +#ifndef GB_TIME_INTERCEPT +#error these unit tests require the symbol GB_TIME_INTERCEPT to be defined +#else +extern "C" +{ + extern time_t gb_time(time_t *timer); + extern struct tm* gb_localtime(const time_t *timer); + extern size_t gb_strftime(char * s, size_t maxsize, const char * format, const struct tm * timeptr); +} +#endif + +#define GBALLOC_H +extern "C" int gballoc_init(void); +extern "C" void gballoc_deinit(void); +extern "C" void* gballoc_malloc(size_t size); +extern "C" void* gballoc_calloc(size_t nmemb, size_t size); +extern "C" void* gballoc_realloc(void* ptr, size_t size); +extern "C" void gballoc_free(void* ptr); + +namespace BASEIMPLEMENTATION +{ + +#define Lock(x) (LOCK_OK + gballocState - gballocState) /*compiler warning about constant in if condition*/ +#define Unlock(x) (LOCK_OK + gballocState - gballocState) +#define Lock_Init() (LOCK_HANDLE)0x42 +#define Lock_Deinit(x) (LOCK_OK + gballocState - gballocState) +#include "gballoc.c" +#undef Lock +#undef Unlock +#undef Lock_Init +#undef Lock_Deinit +}; + +static MICROMOCK_MUTEX_HANDLE g_testByTest; +static MICROMOCK_GLOBAL_SEMAPHORE_HANDLE g_dllByDll; + + +/*these are simple cached variables*/ +static pfModule_Create Logger_Create = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Destroy Logger_Destroy = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ +static pfModule_Receive Logger_Receive = NULL; /*gets assigned in TEST_SUITE_INITIALIZE*/ + +static LOGGER_CONFIG validConfig = +{ + LOGGING_TO_FILE, + "a.txt" +}; +static MESSAGE_BUS_HANDLE validBusHandle = (MESSAGE_BUS_HANDLE)0x1; + +static LOGGER_CONFIG invalidConfig_fileName = +{ + (LOGGER_TYPE)~LOGGING_TO_FILE, + "a.txt" +}; + +static LOGGER_CONFIG invalidConfig_selector = +{ + LOGGING_TO_FILE, + NULL +}; + +#define TIME_IN_STRFTIME "time" +static MESSAGE_HANDLE validMessageHandle = (MESSAGE_HANDLE)0x032; +static unsigned char buffer[3] = { 1,2,3 }; +static CONSTBUFFER validBuffer = { buffer, sizeof(buffer)/sizeof(buffer[0]) }; +TYPED_MOCK_CLASS(CLoggerMocks, CGlobalMock) +{ +public: + + MOCK_STATIC_METHOD_1(, void*, gballoc_malloc, size_t, size) + void* result2 = BASEIMPLEMENTATION::gballoc_malloc(size); + MOCK_METHOD_END(void*, result2); + + MOCK_STATIC_METHOD_1(, void, gballoc_free, void*, ptr) + BASEIMPLEMENTATION::gballoc_free(ptr); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, STRING_HANDLE, STRING_construct, const char*, source) + STRING_HANDLE result2 = (STRING_HANDLE)malloc(4); + MOCK_METHOD_END(STRING_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, STRING_delete, STRING_HANDLE, s) + free(s); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_2(, int, STRING_concat, STRING_HANDLE, s1, const char*, s2) + MOCK_METHOD_END(int, 0); + + MOCK_STATIC_METHOD_2(, int, STRING_concat_with_STRING, STRING_HANDLE, s1, STRING_HANDLE, s2) + MOCK_METHOD_END(int, 0); + + MOCK_STATIC_METHOD_1(, const char*, STRING_c_str, STRING_HANDLE, s) + MOCK_METHOD_END(const char*, "thisIsRandomContent") + + MOCK_STATIC_METHOD_1(, MAP_HANDLE, ConstMap_CloneWriteable, CONSTMAP_HANDLE, handle) + MAP_HANDLE result2 = (MAP_HANDLE)malloc(5); + MOCK_METHOD_END(MAP_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, Map_Destroy, MAP_HANDLE, ptr) + free(ptr); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, STRING_HANDLE, Map_ToJSON, MAP_HANDLE, handle) + STRING_HANDLE result2 = (STRING_HANDLE)malloc(6); + MOCK_METHOD_END(STRING_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message) + CONSTMAP_HANDLE result2 = (CONSTMAP_HANDLE)BASEIMPLEMENTATION::gballoc_malloc(1); + MOCK_METHOD_END(CONSTMAP_HANDLE, result2) + + MOCK_STATIC_METHOD_1(, void, ConstMap_Destroy, CONSTMAP_HANDLE, handle) + free(handle); + MOCK_VOID_METHOD_END() + + MOCK_STATIC_METHOD_1(, const CONSTBUFFER *, Message_GetContent, MESSAGE_HANDLE, message) + const CONSTBUFFER * result2 = &validBuffer; + MOCK_METHOD_END(const CONSTBUFFER *, result2) + + MOCK_STATIC_METHOD_2(, STRING_HANDLE, Base64_Encode_Bytes, const unsigned char*, source, size_t, size); + STRING_HANDLE result2 = (STRING_HANDLE)malloc(7); + MOCK_METHOD_END(STRING_HANDLE, result2); + + MOCK_STATIC_METHOD_2(, FILE*, gb_fopen, const char*, filename, const char*, mode) + FILE* result2 = (FILE*)malloc(8); + MOCK_METHOD_END(FILE*, result2); + + MOCK_STATIC_METHOD_1(, int, gb_fclose, FILE*, file) + free(file); + int result2 = 0; + MOCK_METHOD_END(int, result2); + + MOCK_STATIC_METHOD_3(, int, gb_fseek, FILE *, stream, long int, offset, int, whence) + int result2 = 0; + MOCK_METHOD_END(int, result2); + + MOCK_STATIC_METHOD_1(, long int, gb_ftell, FILE *, stream) + long int result2 = 0; + MOCK_METHOD_END(long int, result2); + + MOCK_STATIC_METHOD_1(, time_t, gb_time, time_t *, timer) + time_t result2 = (time_t)1; /*assume "1" is valid time_t*/ + MOCK_METHOD_END(time_t, result2); + + MOCK_STATIC_METHOD_1(, struct tm*, gb_localtime, const time_t *, timer) + struct tm* result2 = (struct tm*)0x42; + MOCK_METHOD_END(struct tm*, result2); + + MOCK_STATIC_METHOD_4(, size_t, gb_strftime, char*, s, size_t, maxsize, const char *, format, const struct tm *, timeptr) + if (maxsize < strlen(TIME_IN_STRFTIME)+1) + { + ASSERT_FAIL("what is this puny message size!"); + } + else + { + strcpy(s, TIME_IN_STRFTIME); + } + MOCK_METHOD_END(size_t, maxsize); +}; + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void*, gballoc_malloc, size_t, size); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void, gballoc_free, void*, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , STRING_HANDLE, STRING_construct, const char*, s); + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void, STRING_delete, STRING_HANDLE, s); +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , int, STRING_concat, STRING_HANDLE, s1, const char*, s2); +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , int, STRING_concat_with_STRING, STRING_HANDLE, s1, STRING_HANDLE, s2); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , const char*, STRING_c_str, STRING_HANDLE, s); + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , MAP_HANDLE, ConstMap_CloneWriteable, CONSTMAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , STRING_HANDLE, Map_ToJSON, MAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void, Map_Destroy, MAP_HANDLE, ptr); + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , CONSTMAP_HANDLE, Message_GetProperties, MESSAGE_HANDLE, message); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , void, ConstMap_Destroy, CONSTMAP_HANDLE, handle); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , const CONSTBUFFER *, Message_GetContent, MESSAGE_HANDLE, message); + +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , STRING_HANDLE, Base64_Encode_Bytes, const unsigned char*, source, size_t, size); + +DECLARE_GLOBAL_MOCK_METHOD_2(CLoggerMocks, , FILE*, gb_fopen, const char*, filename, const char*, mode); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , int, gb_fclose, FILE*, stream); +DECLARE_GLOBAL_MOCK_METHOD_3(CLoggerMocks, , int, gb_fseek, FILE *, stream, long int, offset, int, whence) +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , long int, gb_ftell, FILE*, stream) + +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , time_t, gb_time, time_t*, timer); +DECLARE_GLOBAL_MOCK_METHOD_1(CLoggerMocks, , struct tm*, gb_localtime, const time_t*, timer); +DECLARE_GLOBAL_MOCK_METHOD_4(CLoggerMocks, , size_t, gb_strftime, char*, s, size_t, maxsize, const char *, format, const struct tm *, timeptr); + + +static void mocks_ResetAllCounters(void) +{ + FOR_EACH_1(RESET_API_COUNTERS, LIST_OF_COUNTED_APIS); +} + +BEGIN_TEST_SUITE(logger_unittests) + + TEST_SUITE_INITIALIZE(TestClassInitialize) + { + INITIALIZE_MEMORY_DEBUG(g_dllByDll); + g_testByTest = MicroMockCreateMutex(); + ASSERT_IS_NOT_NULL(g_testByTest); + + Logger_Create = Module_GetAPIS()->Module_Create; + Logger_Destroy = Module_GetAPIS()->Module_Destroy; + Logger_Receive = Module_GetAPIS()->Module_Receive; + } + + TEST_SUITE_CLEANUP(TestClassCleanup) + { + MicroMockDestroyMutex(g_testByTest); + DEINITIALIZE_MEMORY_DEBUG(g_dllByDll); + } + + TEST_FUNCTION_INITIALIZE(TestMethodInitialize) + { + if (!MicroMockAcquireMutex(g_testByTest)) + { + ASSERT_FAIL("our mutex is ABANDONED. Failure in test framework"); + } + + mocks_ResetAllCounters(); + + } + + TEST_FUNCTION_CLEANUP(TestMethodCleanup) + { + if (!MicroMockReleaseMutex(g_testByTest)) + { + ASSERT_FAIL("failure in test framework at ReleaseMutex"); + } + } + + /*Tests_SRS_LOGGER_02_001: [If busHandle is NULL then Logger_Create shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_with_NULL_busHandle_fails) + { + ///arrange + CLoggerMocks mocks; + + ///act + auto result = Logger_Create(NULL, &validConfig); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_02_002: [If configuration is NULL then Logger_Create shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_with_NULL_config_fails) + { + ///arrange + CLoggerMocks mocks; + + ///act + auto result = Logger_Create(validBusHandle, NULL); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_02_003: [If configuration->selector has a value different than LOGGING_TO_FILE then Logger_Create shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_with_invalid_selector_fails) + { + ///arrange + CLoggerMocks mocks; + + ///act + auto result = Logger_Create(validBusHandle, &invalidConfig_selector); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_02_004: [If configuration->selectee.loggerConfigFile.name is NULL then Logger_Create shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_with_invalid_fileName_fails) + { + ///arrange + CLoggerMocks mocks; + + ///act + auto result = Logger_Create(validBusHandle, &invalidConfig_fileName); + + ///assert + ASSERT_IS_NULL(result); + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_02_005: [Logger_Create shall allocate memory for the below structure.]*/ + /*Tests_SRS_LOGGER_02_006: [Logger_Create shall open the file configuration the filename selectee.loggerConfigFile.name in APPEND mode and assign the result of fopen to fout field. ]*/ + /*Tests_SRS_LOGGER_02_017: [Logger_Create shall add the following JSON value to the existing array of JSON values in the file:]*/ + /*Tests_SRS_LOGGER_02_018: [If the file does not contain a JSON array, then it shall create it.]*/ + /*Tests_SRS_LOGGER_02_008: [Otherwise Logger_Create shall return a non-NULL pointer.]*/ + TEST_FUNCTION(Logger_Create_happy_path_non_existent_file_succeeds) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "r+b")) /*this is opening the file, and because it doesn't exist, it fails*/ + .SetFailReturn((FILE*)NULL); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "w+b")) /*this is opening the file with create*/; + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, 0, SEEK_END)) /*this is going to the end of the file*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_ftell(IGNORED_PTR_ARG)) /*this is getting the file size*/ + .IgnoreArgument(1) + .SetReturn(0); /*zero filesize*/ + + /*here a call to fprintf happens ("[]"), it is captured by a weak verification in ASSERT*/ + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "{\"time\":\"%c\",\"content\":\"Log started\"}]", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, -1, SEEK_END)) /*after getting the STRING that is the beginning of the log, it needs to be appended to the file, this essenially eats the "]" at the end*/ + .IgnoreArgument(1); + + /*here a call to fprintf happens({ + "time":"timeAsPrinted by ctime", + "content": "Log started" +}," + it is captured by a weak verification in ASSERT*/ + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NOT_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 2, CURRENT_API_CALL(gb_fprintf)); + ASSERT_ARE_EQUAL(char_ptr, "[]", all_fprintfs[0]); + ASSERT_ARE_EQUAL(char_ptr, TIME_IN_STRFTIME , all_fprintfs[1]); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_noneexisting_file_fails_when_fprintf_fails_1) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "r+b")) /*this is opening the file, and because it doesn't exist, it fails*/ + .SetFailReturn((FILE*)NULL); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "w+b")) /*this is opening the file with create*/; + + STRICT_EXPECTED_CALL(mocks, gb_fclose(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, 0, SEEK_END)) /*this is going to the end of the file*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_ftell(IGNORED_PTR_ARG)) /*this is getting the file size*/ + .IgnoreArgument(1) + .SetReturn(0); /*zero filesize*/ + + /*here a call to fprintf happens ("[]"), it is captured by a weak verification in ASSERT*/ + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "{\"time\":\"%c\",\"content\":\"Log started\"}]", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, -1, SEEK_END)) /*after getting the STRING that is the beginning of the log, it needs to be appended to the file, this essenially eats the "]" at the end*/ + .IgnoreArgument(1); + + MAKE_FAIL(gb_fprintf, 2); + /*here a call to fprintf happens({ + "time":"timeAsPrinted by ctime", + "content": "Log started" + }," + it is captured by a weak verification in ASSERT*/ + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 2, CURRENT_API_CALL(gb_fprintf)); + ASSERT_ARE_EQUAL(char_ptr, "[]", all_fprintfs[0]); + ASSERT_ARE_EQUAL(char_ptr, TIME_IN_STRFTIME, all_fprintfs[1]); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_noneexisting_file_fails_when_fseek_fails_1) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "r+b")) /*this is opening the file, and because it doesn't exist, it fails*/ + .SetFailReturn((FILE*)NULL); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "w+b")) /*this is opening the file with create*/; + + STRICT_EXPECTED_CALL(mocks, gb_fclose(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, 0, SEEK_END)) /*this is going to the end of the file*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_ftell(IGNORED_PTR_ARG)) /*this is getting the file size*/ + .IgnoreArgument(1) + .SetReturn(0); /*zero filesize*/ + + /*here a call to fprintf happens ("[]"), it is captured by a weak verification in ASSERT*/ + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "{\"time\":\"%c\",\"content\":\"Log started\"}]", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, -1, SEEK_END)) /*after getting the STRING that is the beginning of the log, it needs to be appended to the file, this essenially eats the "]" at the end*/ + .IgnoreArgument(1) + .SetReturn(__LINE__); /*The fseek function returns nonzero only for a request that cannot be satisfied.*/ + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 1, CURRENT_API_CALL(gb_fprintf)); + ASSERT_ARE_EQUAL(char_ptr, "[]", all_fprintfs[0]); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_noneexisting_file_fails_when_strftime_fails) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "r+b")) /*this is opening the file, and because it doesn't exist, it fails*/ + .SetFailReturn((FILE*)NULL); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "w+b")) /*this is opening the file with create*/; + + STRICT_EXPECTED_CALL(mocks, gb_fclose(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, 0, SEEK_END)) /*this is going to the end of the file*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_ftell(IGNORED_PTR_ARG)) /*this is getting the file size*/ + .IgnoreArgument(1) + .SetReturn(0); /*zero filesize*/ + + /*here a call to fprintf happens ("[]"), it is captured by a weak verification in ASSERT*/ + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "{\"time\":\"%c\",\"content\":\"Log started\"}]", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4) + .SetReturn(0); + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 1, CURRENT_API_CALL(gb_fprintf)); + ASSERT_ARE_EQUAL(char_ptr, "[]", all_fprintfs[0]); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_noneexisting_file_fails_when_localtime_fails) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "r+b")) /*this is opening the file, and because it doesn't exist, it fails*/ + .SetFailReturn((FILE*)NULL); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "w+b")) /*this is opening the file with create*/; + + STRICT_EXPECTED_CALL(mocks, gb_fclose(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, 0, SEEK_END)) /*this is going to the end of the file*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_ftell(IGNORED_PTR_ARG)) /*this is getting the file size*/ + .IgnoreArgument(1) + .SetReturn(0); /*zero filesize*/ + + /*here a call to fprintf happens ("[]"), it is captured by a weak verification in ASSERT*/ + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1) + .SetReturn((struct tm*)NULL); /*null pointer if the specified time cannot be converted to local time.*/ + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 1, CURRENT_API_CALL(gb_fprintf)); + ASSERT_ARE_EQUAL(char_ptr, "[]", all_fprintfs[0]); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_noneexisting_file_fails_when_time_fails) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "r+b")) /*this is opening the file, and because it doesn't exist, it fails*/ + .SetFailReturn((FILE*)NULL); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "w+b")) /*this is opening the file with create*/; + + STRICT_EXPECTED_CALL(mocks, gb_fclose(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, 0, SEEK_END)) /*this is going to the end of the file*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_ftell(IGNORED_PTR_ARG)) /*this is getting the file size*/ + .IgnoreArgument(1) + .SetReturn(0); /*zero filesize*/ + + /*here a call to fprintf happens ("[]"), it is captured by a weak verification in ASSERT*/ + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)) /*this is getting the time*/ + .SetReturn((time_t)-1); + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 1, CURRENT_API_CALL(gb_fprintf)); + ASSERT_ARE_EQUAL(char_ptr, "[]", all_fprintfs[0]); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_noneexisting_file_fails_when_fprintf_fails_2) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "r+b")) /*this is opening the file, and because it doesn't exist, it fails*/ + .SetFailReturn((FILE*)NULL); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "w+b")) /*this is opening the file with create*/; + + STRICT_EXPECTED_CALL(mocks, gb_fclose(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, 0, SEEK_END)) /*this is going to the end of the file*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_ftell(IGNORED_PTR_ARG)) /*this is getting the file size*/ + .IgnoreArgument(1) + .SetReturn(0); /*zero filesize*/ + + MAKE_FAIL(gb_fprintf, 1); + /*here a call to fprintf happens ("[]"), it is captured by a weak verification in ASSERT*/ + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 1, CURRENT_API_CALL(gb_fprintf)); + ASSERT_ARE_EQUAL(char_ptr, "[]", all_fprintfs[0]); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_noneexisting_file_fails_when_ftell_fails) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "r+b")) /*this is opening the file, and because it doesn't exist, it fails*/ + .SetFailReturn((FILE*)NULL); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "w+b")) /*this is opening the file with create*/; + + STRICT_EXPECTED_CALL(mocks, gb_fclose(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, 0, SEEK_END)) /*this is going to the end of the file*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_ftell(IGNORED_PTR_ARG)) /*this is getting the file size*/ + .IgnoreArgument(1) + .SetReturn(-1L); + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_noneexisting_file_fails_when_fseek_fails_2) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "r+b")) /*this is opening the file, and because it doesn't exist, it fails*/ + .SetFailReturn((FILE*)NULL); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "w+b")) /*this is opening the file with create*/; + + STRICT_EXPECTED_CALL(mocks, gb_fclose(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, 0, SEEK_END)) /*this is going to the end of the file*/ + .IgnoreArgument(1) + .SetReturn(~0);/*The fseek function returns nonzero only for a request that cannot be satisfied*/ + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + /*Tests_SRS_LOGGER_02_020: [If the file selectee.loggerConfigFile.name does not exist, it shall be created.]*/ + /*Tests_SRS_LOGGER_02_021: [If creating selectee.loggerConfigFile.name fails then Logger_Create shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_noneexisting_file_fails_when_fopen_fails) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "r+b")) /*this is opening the file*/ + .SetFailReturn((FILE*)NULL); + + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "w+b")) /*this is trying to create the file*/ + .SetFailReturn((FILE*)NULL); + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_007: [If Logger_Create encounters any errors while creating the LOGGER_HANDLE_DATA then it shall fail and return NULL.]*/ + TEST_FUNCTION(Logger_Create_noneexisting_file_fails_when_malloc_fails) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1) + .SetFailReturn((void*)NULL); + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_005: [Logger_Create shall allocate memory for the below structure.]*/ + /*Tests_SRS_LOGGER_02_006: [Logger_Create shall open the file configuration the filename selectee.loggerConfigFile.name in APPEND mode and assign the result of fopen to fout field. ]*/ + /*Tests_SRS_LOGGER_02_017: [Logger_Create shall add the following JSON value to the existing array of JSON values in the file:]*/ + TEST_FUNCTION(Logger_Create_happy_path_existing_file) + { + ///arrange + CLoggerMocks mocks; + + STRICT_EXPECTED_CALL(mocks, gballoc_malloc(IGNORED_NUM_ARG)) /*this is the handle*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, gb_fopen(validConfig.selectee.loggerConfigFile.name, "r+b")); /*this is opening the file*/ + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, 0, SEEK_END)) /*this is going to the end of the file*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_ftell(IGNORED_PTR_ARG)) /*this is getting the file size*/ + .IgnoreArgument(1) + .SetReturn(2); /*non-zero filesize*/ + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, ",{\"time\":\"%c\",\"content\":\"Log started\"}]", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, -1, SEEK_END)) /*after getting the STRING that is the beginning of the log, it needs to be appended to the file, this essenially eats the "]" at the end*/ + .IgnoreArgument(1); + + /*here a call to fprintf happens({ + "time":"timeAsPrinted by ctime", + "content": "Log started" + }]" + it is captured by a weak verification in ASSERT*/ + + ///act + auto handle = Logger_Create(validBusHandle, &validConfig); + + ///assert + ASSERT_IS_NOT_NULL(handle); + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 1, CURRENT_API_CALL(gb_fprintf)); + ASSERT_ARE_EQUAL(char_ptr, TIME_IN_STRFTIME, all_fprintfs[0]); + + ///cleanup + Logger_Destroy(handle); + + } + + /*Tests_SRS_LOGGER_02_009: [If moduleHandle is NULL then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_with_NULL_modulehandle_fails) + { + ///arrange + CLoggerMocks mocks; + + ///act + Logger_Receive(NULL, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + + } + + /*Tests_SRS_LOGGER_02_010: [If messageHandle is NULL then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_with_NULL_messageHandle_fails) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + + ///act + Logger_Receive(moduleHandle, NULL); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_011: [Logger_Receive shall write in the fout FILE the following information in JSON format:]*/ + /*Tests_SRS_LOGGER_02_013: [Logger_Receive shall return.]*/ + TEST_FUNCTION(Logger_Receive_happy_path) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetContent(validMessageHandle)); /*this is getting the content*/ + + STRICT_EXPECTED_CALL(mocks, Base64_Encode_Bytes(buffer, sizeof(buffer) / sizeof(buffer[0]))); /*this is getting a STRING_HANDLE that is the base64 encode of the bytes*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(",{\"time\":\"")); /*this is the actual JSON object building*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the real time to the json*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\",\"properties\":")) /*this is adding the "properties":" string*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of MapToJSON*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, ",\"content\":\"")) /*this is adding the ,"content":"*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of base64_encode*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\"}]")) /*this closes the JSON*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) /*this is harvesting the const char* of the json string*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, -1, SEEK_END)) /*this is rewinding the file by 1 character*/ + .IgnoreArgument(1); + + /*here a call to fprintf happens({ + "time":"timeAsPrinted by ctime", + "content": "Log started" + }," + it is captured by a weak verification in ASSERT*/ + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 1, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_fprintf_fails) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetContent(validMessageHandle)); /*this is getting the content*/ + + STRICT_EXPECTED_CALL(mocks, Base64_Encode_Bytes(buffer, sizeof(buffer) / sizeof(buffer[0]))); /*this is getting a STRING_HANDLE that is the base64 encode of the bytes*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(",{\"time\":\"")); /*this is the actual JSON object building*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the real time to the json*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\",\"properties\":")) /*this is adding the "properties":" string*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of MapToJSON*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, ",\"content\":\"")) /*this is adding the ,"content":"*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of base64_encode*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\"}]")) /*this closes the JSON*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) /*this is harvesting the const char* of the json string*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, -1, SEEK_END)) /*this is rewinding the file by 1 character*/ + .IgnoreArgument(1); + + MAKE_FAIL(gb_fprintf, 1); + /*here a call to fprintf happens({ + "time":"timeAsPrinted by ctime", + "content": "Log started" + }," + it is captured by a weak verification in ASSERT*/ + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 1, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_fseek_fails) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetContent(validMessageHandle)); /*this is getting the content*/ + + STRICT_EXPECTED_CALL(mocks, Base64_Encode_Bytes(buffer, sizeof(buffer) / sizeof(buffer[0]))); /*this is getting a STRING_HANDLE that is the base64 encode of the bytes*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(",{\"time\":\"")); /*this is the actual JSON object building*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the real time to the json*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\",\"properties\":")) /*this is adding the "properties":" string*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of MapToJSON*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, ",\"content\":\"")) /*this is adding the ,"content":"*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of base64_encode*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\"}]")) /*this closes the JSON*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_c_str(IGNORED_PTR_ARG)) /*this is harvesting the const char* of the json string*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, -1, SEEK_END)) /*this is rewinding the file by 1 character*/ + .IgnoreArgument(1) + .SetReturn(__LINE__); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_STRING_concat_fails_1) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetContent(validMessageHandle)); /*this is getting the content*/ + + STRICT_EXPECTED_CALL(mocks, Base64_Encode_Bytes(buffer, sizeof(buffer) / sizeof(buffer[0]))); /*this is getting a STRING_HANDLE that is the base64 encode of the bytes*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(",{\"time\":\"")); /*this is the actual JSON object building*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the real time to the json*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\",\"properties\":")) /*this is adding the "properties":" string*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of MapToJSON*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, ",\"content\":\"")) /*this is adding the ,"content":"*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of base64_encode*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\"}]")) /*this closes the JSON*/ + .IgnoreArgument(1) + .SetFailReturn(__LINE__); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_STRING_concat_with_STRING_fails_1) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetContent(validMessageHandle)); /*this is getting the content*/ + + STRICT_EXPECTED_CALL(mocks, Base64_Encode_Bytes(buffer, sizeof(buffer) / sizeof(buffer[0]))); /*this is getting a STRING_HANDLE that is the base64 encode of the bytes*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(",{\"time\":\"")); /*this is the actual JSON object building*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the real time to the json*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\",\"properties\":")) /*this is adding the "properties":" string*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of MapToJSON*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, ",\"content\":\"")) /*this is adding the ,"content":"*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of base64_encode*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn(__LINE__); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_STRING_concat_fails_2) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetContent(validMessageHandle)); /*this is getting the content*/ + + STRICT_EXPECTED_CALL(mocks, Base64_Encode_Bytes(buffer, sizeof(buffer) / sizeof(buffer[0]))); /*this is getting a STRING_HANDLE that is the base64 encode of the bytes*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(",{\"time\":\"")); /*this is the actual JSON object building*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the real time to the json*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\",\"properties\":")) /*this is adding the "properties":" string*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of MapToJSON*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, ",\"content\":\"")) /*this is adding the ,"content":"*/ + .IgnoreArgument(1) + .SetFailReturn(__LINE__); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_STRING_concat_with_STRING_fails_2) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetContent(validMessageHandle)); /*this is getting the content*/ + + STRICT_EXPECTED_CALL(mocks, Base64_Encode_Bytes(buffer, sizeof(buffer) / sizeof(buffer[0]))); /*this is getting a STRING_HANDLE that is the base64 encode of the bytes*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(",{\"time\":\"")); /*this is the actual JSON object building*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the real time to the json*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\",\"properties\":")) /*this is adding the "properties":" string*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_concat_with_STRING(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the result of MapToJSON*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn(__LINE__); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_STRING_concat_fails_3) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetContent(validMessageHandle)); /*this is getting the content*/ + + STRICT_EXPECTED_CALL(mocks, Base64_Encode_Bytes(buffer, sizeof(buffer) / sizeof(buffer[0]))); /*this is getting a STRING_HANDLE that is the base64 encode of the bytes*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(",{\"time\":\"")); /*this is the actual JSON object building*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the real time to the json*/ + .IgnoreArgument(1) + .IgnoreArgument(2); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, "\",\"properties\":")) /*this is adding the "properties":" string*/ + .IgnoreArgument(1) + .SetFailReturn(__LINE__); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_STRING_concat_fails_4) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetContent(validMessageHandle)); /*this is getting the content*/ + + STRICT_EXPECTED_CALL(mocks, Base64_Encode_Bytes(buffer, sizeof(buffer) / sizeof(buffer[0]))); /*this is getting a STRING_HANDLE that is the base64 encode of the bytes*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(",{\"time\":\"")); /*this is the actual JSON object building*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_concat(IGNORED_PTR_ARG, IGNORED_PTR_ARG)) /*this is adding the real time to the json*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .SetFailReturn(__LINE__); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_STRING_construct_fails) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetContent(validMessageHandle)); /*this is getting the content*/ + + STRICT_EXPECTED_CALL(mocks, Base64_Encode_Bytes(buffer, sizeof(buffer) / sizeof(buffer[0]))); /*this is getting a STRING_HANDLE that is the base64 encode of the bytes*/ + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, STRING_construct(",{\"time\":\"")) /*this is the actual JSON object building*/ + .SetFailReturn((STRING_HANDLE)NULL); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_Base64_Encode_Bytes_fails) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, STRING_delete(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Message_GetContent(validMessageHandle)); /*this is getting the content*/ + + STRICT_EXPECTED_CALL(mocks, Base64_Encode_Bytes(buffer, sizeof(buffer) / sizeof(buffer[0]))) /*this is getting a STRING_HANDLE that is the base64 encode of the bytes*/ + .SetFailReturn((STRING_HANDLE)NULL); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_Map_ToJSON_fails) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1); + STRICT_EXPECTED_CALL(mocks, Map_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, Map_ToJSON(IGNORED_PTR_ARG)) /*this is getting a STRING_HANDLE that is the MAP as JSON*/ + .IgnoreArgument(1) + .SetFailReturn((STRING_HANDLE)NULL); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_ConstMap_CloneWriteable_fails) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, Message_GetProperties(validMessageHandle)); /*this is getting the properties from the message*/ + STRICT_EXPECTED_CALL(mocks, ConstMap_Destroy(IGNORED_PTR_ARG)) + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, ConstMap_CloneWriteable(IGNORED_PTR_ARG)) /*this is getting the properties in a writeable map, because ConstMap doesn't have ToJSON*/ + .IgnoreArgument(1) + .SetFailReturn((MAP_HANDLE)NULL); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_gb_strftime_fails) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, "%c", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4) + .SetFailReturn(0); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_gb_localtime_fails) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1) + .SetFailReturn((struct tm*)NULL); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_012: [If producing the JSON format or writing it to the file fails, then Logger_Receive shall fail and return.]*/ + TEST_FUNCTION(Logger_Receive_fails_when_gb_time_fails) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)) /*this is getting the time*/ + .SetFailReturn((time_t)-1); + + ///act + Logger_Receive(moduleHandle, validMessageHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + ASSERT_ARE_EQUAL(size_t, 0, CURRENT_API_CALL(gb_fprintf)); + + ///cleanup + Logger_Destroy(moduleHandle); + + } + + /*Tests_SRS_LOGGER_02_014: [If moduleHandle is NULL then Logger_Destroy shall return.] */ + TEST_FUNCTION(Logger_Destroy_with_NULL_parameter_returns) + { + ///arrange + CLoggerMocks mocks; + + ///act + Logger_Destroy(NULL); + + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_02_019: [Logger_Destroy shall add to the log file the following end of log JSON object:]*/ + /*Tests_SRS_LOGGER_02_015: [Otherwise Logger_Destroy shall unuse all used resources.]*/ + TEST_FUNCTION(Logger_Destroy_happy_path) + { + ///arrange + CLoggerMocks mocks; + auto moduleHandle = Logger_Create(validBusHandle, &validConfig); + mocks.ResetAllCalls(); + mocks_ResetAllCounters(); + + STRICT_EXPECTED_CALL(mocks, gb_time(NULL)); /*this is getting the time*/ + + STRICT_EXPECTED_CALL(mocks, gb_localtime(IGNORED_PTR_ARG)) /*this is transforming the time from time_t to struct tm* */ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gb_strftime(IGNORED_PTR_ARG, IGNORED_NUM_ARG, ",{\"time\":\"%c\",\"content\":\"Log stopped\"}]", IGNORED_PTR_ARG)) /*this is building a JSON object in timetemp*/ + .IgnoreArgument(1) + .IgnoreArgument(2) + .IgnoreArgument(4); + + STRICT_EXPECTED_CALL(mocks, gb_fseek(IGNORED_PTR_ARG, -1, SEEK_END)) /*after getting the STRING that is the beginning of the log, it needs to be appended to the file, this essenially eats the "," from the last entry*/ + .IgnoreArgument(1); + + /*here a call to fprintf happens({ + "time":"timeAsPrinted by ctime", + "content": "Log stopped" + }]" + it is captured by a weak verification in ASSERT*/ + + STRICT_EXPECTED_CALL(mocks, gb_fclose(IGNORED_PTR_ARG)) /*this closes the file opened in _create*/ + .IgnoreArgument(1); + + STRICT_EXPECTED_CALL(mocks, gballoc_free(IGNORED_PTR_ARG)) /*this frees the memory allocated for the handle data*/ + .IgnoreArgument(1); + + ///act + Logger_Destroy(moduleHandle); + + ///assert + mocks.AssertActualAndExpectedCalls(); + + ///cleanup + } + + /*Tests_SRS_LOGGER_02_016: [Module_GetAPIS shall return a non-NULL pointer to a structure of type MODULE_APIS that has all fields non-NULL.]*/ + TEST_FUNCTION(Module_GetAPIS_returns_non_NULL_and_non_NULL_fields) + { + ///arrrange + + ///act + auto result = Module_GetAPIS(); + + ///assert + ASSERT_IS_NOT_NULL(result); + ASSERT_IS_NOT_NULL(result->Module_Create); + ASSERT_IS_NOT_NULL(result->Module_Destroy); + ASSERT_IS_NOT_NULL(result->Module_Receive); + } +END_TEST_SUITE(logger_unittests) diff --git a/modules/logger/tests/logger_unittests/main.c b/modules/logger/tests/logger_unittests/main.c new file mode 100644 index 00000000..094c3e11 --- /dev/null +++ b/modules/logger/tests/logger_unittests/main.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "testrunnerswitcher.h" + +int main(void) +{ + size_t failedTestCount = 0; + RUN_TEST_SUITE(logger_unittests, failedTestCount); + return failedTestCount; +} diff --git a/modules/simulated_device/CMakeLists.txt b/modules/simulated_device/CMakeLists.txt new file mode 100644 index 00000000..506d7993 --- /dev/null +++ b/modules/simulated_device/CMakeLists.txt @@ -0,0 +1,72 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#this is CMakeLists.txt for the simulated_device module +cmake_minimum_required(VERSION 2.8.11) + +set(simulated_device_sources + ./src/simulated_device.c +) + +set(simulated_device_headers + ./inc/simulated_device.h +) + +set(simulated_device_static_sources + ${simulated_device_sources} +) + +set(simulated_device_static_headers + ${simulated_device_headers} +) + +set(simulated_device_hl_sources + ./src/simulated_device_hl.c +) + +set(simulated_device_hl_headers + ./inc/simulated_device_hl.h +) + +set(simulated_device_hl_static_sources + ${simulated_device_hl_sources} +) + +set(simulated_device_hl_static_headers + ${simulated_device_hl_headers} +) + + +include_directories(./inc) +include_directories(${GW_INC}) + +#this builds the simulated_device dynamic library +add_library(simulated_device MODULE ${simulated_device_sources} ${simulated_device_headers}) +target_link_libraries(simulated_device gateway) + +#this builds the simulated_device static library +add_library(simulated_device_static ${simulated_device_static_sources} ${simulated_device_static_headers}) +target_compile_definitions(simulated_device_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(simulated_device_static gateway) + +#this builds the simulated_device dynamic library (by default it uses simulated_device module linked statically) +add_library(simulated_device_hl MODULE ${simulated_device_hl_sources} ${simulated_device_hl_headers}) +target_link_libraries(simulated_device_hl simulated_device_static gateway) + +#this builds the simulated_device static library (by default it uses simulated_device module linked statically) +add_library(simulated_device_hl_static ${simulated_device_hl_static_sources} ${simulated_device_hl_static_headers}) +target_compile_definitions(simulated_device_hl_static PRIVATE BUILD_MODULE_TYPE_STATIC) +target_link_libraries(simulated_device_hl_static simulated_device_static gateway) + +linkSharedUtil(simulated_device) +linkSharedUtil(simulated_device_static) +linkSharedUtil(simulated_device_hl) +linkSharedUtil(simulated_device_hl_static) + +add_module_to_solution(simulated_device) + +if(install_executables) + install(TARGETS simulated_device LIBRARY DESTINATION lib) + install(TARGETS simulated_device_hl LIBRARY DESTINATION lib) +endif() + diff --git a/modules/simulated_device/README.md b/modules/simulated_device/README.md new file mode 100644 index 00000000..d0b0f349 --- /dev/null +++ b/modules/simulated_device/README.md @@ -0,0 +1 @@ +# Simulated BLE (Bluetooth Low Energy) Module \ No newline at end of file diff --git a/modules/simulated_device/devdoc/simulated_device_hl.md b/modules/simulated_device/devdoc/simulated_device_hl.md new file mode 100644 index 00000000..46b63271 --- /dev/null +++ b/modules/simulated_device/devdoc/simulated_device_hl.md @@ -0,0 +1,58 @@ +# SIMULATED DEVICE Module HL Requirements + +## Overview +This document describes the SIMULATED DEVICE high level module. This module adapts +the existing lower layer SIMULATED DEVICE module for use with the Gateway HL library. +It is mostly a passthrough to the existing module, with a specialized create +function to interpret the serialized JSON module arguments. + +## Reference + +[module.h](../../../../devdoc/module.md) + +### Expected Arguments + +The argument to this module is a JSON object with the following structure: +```json +{ + "macAddress" : "" +} +``` +### Example Arguments +```json +{ + "macAddress" : "01:01:01:01:01:01" +} +``` + +##Exposed API +```c +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void); +``` + +## Module_GetAPIs + +This is the primary public interface for the module. It returns a pointer to +the `MODULE_APIS` structure containing the implementation functions for this +module. `Module_GetAPIs` returns a non-`NULL` pointer to a MODULE_APIS structure. +The `MODULE_APIS` structure shall have non-`NULL` `Module_Create`, `Module_Destroy`, +and `Module_Receive` fields. + +## SimulatedDevice_HL_Create +```C +MODULE_HANDLE SimulatedDevice_HL_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration); +``` +This function creates the SIMULATED DEVICE HL module. This module reads a JSON +object and passes the macAddress to the underlying SIMULATED DEVICE module's _Create. + +## SimulatedDevice_HL_Destroy +```C +static void SimulatedDevice_HL_Destroy(MODULE_HANDLE moduleHandle); +``` +Frees all used resources. + +## SimulatedDevice_HL_Receive +```C +static void SimulatedDevice_HL_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle); +``` +Calls underlying module (SIMULATED DEVICE) _Receive function. \ No newline at end of file diff --git a/modules/simulated_device/inc/simulated_device.h b/modules/simulated_device/inc/simulated_device.h new file mode 100644 index 00000000..fffd993d --- /dev/null +++ b/modules/simulated_device/inc/simulated_device.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SIMULATED_DEVICE_H +#define SIMULATED_DEVICE_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(SIMULATED_DEVICE_MODULE)(void); + +#ifdef __cplusplus +} +#endif + +#endif /*SIMULATED_DEVICE_H*/ diff --git a/modules/simulated_device/inc/simulated_device_hl.h b/modules/simulated_device/inc/simulated_device_hl.h new file mode 100644 index 00000000..786b9bbc --- /dev/null +++ b/modules/simulated_device/inc/simulated_device_hl.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SIMULATED_DEVICE_HL_H +#define SIMULATED_DEVICE_HL_H + +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(SIMULATED_DEVICE_HL_MODULE)(void); + +#ifdef __cplusplus +} +#endif + + +#endif /*SIMULATED_DEVICE_HL_H*/ diff --git a/modules/simulated_device/src/simulated_device.c b/modules/simulated_device/src/simulated_device.c new file mode 100644 index 00000000..693b31cd --- /dev/null +++ b/modules/simulated_device/src/simulated_device.c @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#include "simulated_device.h" +#include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "message.h" +#include "messageproperties.h" + +#include "module.h" +#include "message_bus.h" + +typedef struct SIMULATEDDEVICE_DATA_TAG +{ + MESSAGE_BUS_HANDLE bus; + THREAD_HANDLE simulatedDeviceThread; + const char * fakeMacAddress; + unsigned int simulatedDeviceRunning : 1; +} SIMULATEDDEVICE_DATA; + +static char msgText[1024]; + +static void SimulatedDevice_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + return; +} + +static void SimulatedDevice_Destroy(MODULE_HANDLE moduleHandle) +{ + if (moduleHandle == NULL) + { + LogError("Attempt to destroy NULL module"); + } + else + { + SIMULATEDDEVICE_DATA* module_data = (SIMULATEDDEVICE_DATA*)moduleHandle; + int result; + + /* Tell thread to stop */ + module_data->simulatedDeviceRunning = 0; + /* join the thread */ + ThreadAPI_Join(module_data->simulatedDeviceThread, &result); + /* free module data */ + free((void*)module_data->fakeMacAddress); + free(module_data); + } +} + +static int simulated_device_worker(void * user_data) +{ + SIMULATEDDEVICE_DATA* module_data = (SIMULATEDDEVICE_DATA*)user_data; + double avgTemperature = 10.0; + double additionalTemp = 0.0; + double maxSpeed = 40.0; + + if (user_data != NULL) + { + + while (module_data->simulatedDeviceRunning) + { + MESSAGE_CONFIG newMessageCfg; + MAP_HANDLE newProperties = Map_Create(NULL); + if (newProperties == NULL) + { + LogError("Failed to create message properties"); + } + + else + { + if (Map_Add(newProperties, GW_SOURCE_PROPERTY, GW_SOURCE_BLE_TELEMETRY) != MAP_OK) + { + LogError("Failed to set source property"); + } + else if (Map_Add(newProperties, GW_MAC_ADDRESS_PROPERTY, module_data->fakeMacAddress) != MAP_OK) + { + LogError("Failed to set source property"); + } + else + { + newMessageCfg.sourceProperties = newProperties; + if ((avgTemperature + additionalTemp) > maxSpeed) + additionalTemp = 0.0; + + if (sprintf_s(msgText, sizeof(msgText), "{\"temperature\": %.2f}", avgTemperature + additionalTemp) < 0) + { + LogError("Failed to set message text"); + } + else + { + newMessageCfg.size = strlen(msgText); + newMessageCfg.source = (const unsigned char*)msgText; + + MESSAGE_HANDLE newMessage = Message_Create(&newMessageCfg); + if (newMessage == NULL) + { + LogError("Failed to create new message"); + } + else + { + if (MessageBus_Publish(module_data->bus, newMessage) != MESSAGE_BUS_OK) + { + LogError("Failed to create new message"); + } + + additionalTemp += 1.0; + Message_Destroy(newMessage); + } + } + } + Map_Destroy(newProperties); + } + ThreadAPI_Sleep(10000); + } + } + + return 0; +} + + +static MODULE_HANDLE SimulatedDevice_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + SIMULATEDDEVICE_DATA * result; + if (busHandle == NULL || configuration == NULL) + { + LogError("invalid SIMULATED DEVICE module args."); + result = NULL; + } + else + { + /* allocate module data struct */ + result = (SIMULATEDDEVICE_DATA*)malloc(sizeof(SIMULATEDDEVICE_DATA)); + if (result == NULL) + { + LogError("couldn't allocate memory for BLE Module"); + } + else + { + /* save the message bus */ + result->bus = busHandle; + /* set module is running to true */ + result->simulatedDeviceRunning = 1; + /* save fake MacAddress */ + char * newFakeAdress; + int status = mallocAndStrcpy_s(&newFakeAdress, configuration); + + if (status != 0) + { + LogError("MacAddress did not copy"); + } + else + { + result->fakeMacAddress = newFakeAdress; + /* OK to start */ + /* Create a fake data thread. */ + if (ThreadAPI_Create( + &(result->simulatedDeviceThread), + simulated_device_worker, + (void*)result) != THREADAPI_OK) + { + LogError("ThreadAPI_Create failed"); + free(result); + result = NULL; + } + else + { + /* Thread started, module created, all complete.*/ + } + } + + } + } + return result; +} + +/* + * Required for all modules: the public API and the designated implementation functions. + */ +static const MODULE_APIS SimulatedDevice_APIS_all = +{ + SimulatedDevice_Create, + SimulatedDevice_Destroy, + SimulatedDevice_Receive +}; + +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(SIMULATED_DEVICE_MODULE)(void) +#else +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + return &SimulatedDevice_APIS_all; +} diff --git a/modules/simulated_device/src/simulated_device_hl.c b/modules/simulated_device/src/simulated_device_hl.c new file mode 100644 index 00000000..bd15b747 --- /dev/null +++ b/modules/simulated_device/src/simulated_device_hl.c @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#include "simulated_device.h" +#include "simulated_device_hl.h" +#include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/crt_abstractions.h" +#include "message.h" +#include "messageproperties.h" + +#include "module.h" +#include "message_bus.h" +#include "parson.h" + +static void SimulatedDevice_HL_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle) +{ + MODULE_STATIC_GETAPIS(SIMULATED_DEVICE_MODULE)()->Module_Receive(moduleHandle, messageHandle); +} + +static void SimulatedDevice_HL_Destroy(MODULE_HANDLE moduleHandle) +{ + MODULE_STATIC_GETAPIS(SIMULATED_DEVICE_MODULE)()->Module_Destroy(moduleHandle); +} + +static MODULE_HANDLE SimulatedDevice_HL_Create(MESSAGE_BUS_HANDLE busHandle, const void* configuration) +{ + MODULE_HANDLE result; + if (busHandle == NULL || configuration == NULL) + { + LogError("invalid SIMULATED_DEVICE_HL module args."); + result = NULL; + } + else + { + JSON_Value* json = json_parse_string((const char*)configuration); + if (json == NULL) + { + LogError("unable to json_parse_string"); + result = NULL; + } + else + { + JSON_Object* root = json_value_get_object(json); + if (root == NULL) + { + LogError("unable to json_value_get_object"); + result = NULL; + } + else + { + const char* macAddress = json_object_get_string(root, "macAddress"); + if (macAddress == NULL) + { + LogError("unable to json_object_get_string"); + result = NULL; + } + else + { + result = MODULE_STATIC_GETAPIS(SIMULATED_DEVICE_MODULE)()->Module_Create(busHandle, macAddress); + } + } + json_value_free(json); + } + } + return result; +} + +/* + * Required for all modules: the public API and the designated implementation functions. + */ +static const MODULE_APIS SimulatedDevice_HL_APIS_all = +{ + SimulatedDevice_HL_Create, + SimulatedDevice_HL_Destroy, + SimulatedDevice_HL_Receive +}; + +#ifdef BUILD_MODULE_TYPE_STATIC +MODULE_EXPORT const MODULE_APIS* MODULE_STATIC_GETAPIS(SIMULATED_DEVICE_HL_MODULE)(void) +#else +MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void) +#endif +{ + return &SimulatedDevice_HL_APIS_all; +} diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt new file mode 100644 index 00000000..f79023d2 --- /dev/null +++ b/samples/CMakeLists.txt @@ -0,0 +1,12 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) +#this is CMakeLists for samples folder + +add_subdirectory(hello_world) +add_subdirectory(simulated_device_cloud_upload) + +if(LINUX) + add_subdirectory(ble_gateway_hl) +endif() \ No newline at end of file diff --git a/samples/README.md b/samples/README.md new file mode 100644 index 00000000..9435cd94 --- /dev/null +++ b/samples/README.md @@ -0,0 +1 @@ +# Samples \ No newline at end of file diff --git a/samples/ble_gateway_hl/CMakeLists.txt b/samples/ble_gateway_hl/CMakeLists.txt new file mode 100644 index 00000000..d6c4ac5b --- /dev/null +++ b/samples/ble_gateway_hl/CMakeLists.txt @@ -0,0 +1,45 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) + +################################################ +# Include GIO headers/libs +################################################ +find_package(PkgConfig REQUIRED) +pkg_search_module(GWGIOUNIX REQUIRED gio-unix-2.0) + +include_directories(${GWGIOUNIX_INCLUDE_DIRS}) +set(LIBS ${GWGIOUNIX_LIBRARIES}) + +set(ble_gateway_hl_sources + ./src/main.c +) +set(ble_gateway_hl_headers +) + +include_directories( + ../../inc + ${GW_INC} +) +set(LIBS ${LIBS} gateway) + +# Raspberry Pi 3 doesn't see 'signal' types unless this symbol is defined +add_definitions(-D_POSIX_C_SOURCE=1) + +add_executable(ble_gateway_hl ${ble_gateway_hl_headers} ${ble_gateway_hl_sources}) +target_link_libraries(ble_gateway_hl ${LIBS}) + +# make ble_gateway_hl depend on the ble_hl module; since this module +# is a runtime dependency, changes to code in ble_hl will not get built +# when building the 'ble_gateway_hl' target; this explicit dependency +# declaration will cause a build +add_dependencies(ble_gateway_hl ble_hl ble_printer identity_map_hl logger_hl iothubhttp_hl) + +linkSharedUtil(ble_gateway_hl) + +add_sample_to_solution(ble_gateway_hl) + +if(LINUX) + add_subdirectory(ble_printer) +endif() diff --git a/samples/ble_gateway_hl/ble_printer/CMakeLists.txt b/samples/ble_gateway_hl/ble_printer/CMakeLists.txt new file mode 100644 index 00000000..9cbf6cd3 --- /dev/null +++ b/samples/ble_gateway_hl/ble_printer/CMakeLists.txt @@ -0,0 +1,32 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) + +################################################ +# Include GIO headers/libs +################################################ +find_package(PkgConfig REQUIRED) +pkg_search_module(GWGIOUNIX REQUIRED gio-unix-2.0) + +include_directories(${GWGIOUNIX_INCLUDE_DIRS}) +set(LIBS ${GWGIOUNIX_LIBRARIES}) + +set(ble_printer_sources + ./src/ble_printer.c +) +set(ble_printer_headers + ./inc/ble_printer.h +) + +include_directories( + ../../../modules/ble/inc + ../../../modules/common + ./inc + ${GW_INC} +) +set(LIBS ${LIBS} gateway m) + +add_library(ble_printer MODULE ${ble_printer_sources} ${ble_printer_headers}) +target_link_libraries(ble_printer ${LIBS}) +linkSharedUtil(ble_printer) diff --git a/samples/ble_gateway_hl/ble_printer/inc/ble_printer.h b/samples/ble_gateway_hl/ble_printer/inc/ble_printer.h new file mode 100644 index 00000000..ed39f55d --- /dev/null +++ b/samples/ble_gateway_hl/ble_printer/inc/ble_printer.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef BLE_PRINTER_H +#define BLE_PRINTER_H + +/** +* Including module.h dictates that: +* +* MODULE_EXPORT const MODULE_APIS* Module_GetAPIS(void); +* +* is implemented by the module. +*/ +#include "module.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*BLE_PRINTER_H*/ \ No newline at end of file diff --git a/samples/ble_gateway_hl/ble_printer/src/ble_printer.c b/samples/ble_gateway_hl/ble_printer/src/ble_printer.c new file mode 100644 index 00000000..8da810c2 --- /dev/null +++ b/samples/ble_gateway_hl/ble_printer/src/ble_printer.c @@ -0,0 +1,220 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include +#include +#include + +#include + +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif + +#include "azure_c_shared_utility/gballoc.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/constmap.h" +#include "azure_c_shared_utility/constbuffer.h" + +#include "module.h" +#include "message.h" +#include "message_bus.h" +#include "ble.h" +#include "ble_printer.h" +#include "messageproperties.h" + +typedef void(*MESSAGE_PRINTER)(const char* name, const char* timestamp, const CONSTBUFFER* buffer); + +static void print_string(const char* name, const char* timestamp, const CONSTBUFFER* buffer); +static void print_temperature(const char* name, const char* timestamp, const CONSTBUFFER* buffer); +static void print_default(const char* name, const char* timestamp, const CONSTBUFFER* buffer); + +typedef struct DIPATCH_ENTRY_TAG +{ + const char* name; + const char* characteristic_uuid; + MESSAGE_PRINTER message_printer; +}DIPATCH_ENTRY; + +// setup the message printers +DIPATCH_ENTRY g_dispatch_entries[] = +{ + { + "Model Number", + "00002A24-0000-1000-8000-00805F9B34FB", + print_string + }, + { + "Serial Number", + "00002A25-0000-1000-8000-00805F9B34FB", + print_string + }, + { + "Serial Number", + "00002A25-0000-1000-8000-00805F9B34FB", + print_string + }, + { + "Firmware Revision Number", + "00002A26-0000-1000-8000-00805F9B34FB", + print_string + }, + { + "Hardware Revision Number", + "00002A27-0000-1000-8000-00805F9B34FB", + print_string + }, + { + "Software Revision Number", + "00002A28-0000-1000-8000-00805F9B34FB", + print_string + }, + { + "Manufacturer", + "00002A29-0000-1000-8000-00805F9B34FB", + print_string + }, + { + "Temperature", + "F000AA01-0451-4000-B000-000000000000", + print_temperature + } +}; + +size_t g_dispatch_entries_length = sizeof(g_dispatch_entries) / sizeof(g_dispatch_entries[0]); + +MODULE_HANDLE BLEPrinter_Create(MESSAGE_BUS_HANDLE bus, const void* configuration) +{ + return (MODULE_HANDLE)0x42; +} + +void BLEPrinter_Receive(MODULE_HANDLE module, MESSAGE_HANDLE message) +{ + if (message != NULL) + { + CONSTMAP_HANDLE props = Message_GetProperties(message); + if (props != NULL) + { + const char* source = ConstMap_GetValue(props, GW_SOURCE_PROPERTY); + if (source != NULL && strcmp(source, GW_SOURCE_BLE_TELEMETRY) == 0) + { + const char* ble_controller_id = ConstMap_GetValue(props, GW_BLE_CONTROLLER_INDEX_PROPERTY); + const char* mac_address_str = ConstMap_GetValue(props, GW_MAC_ADDRESS_PROPERTY); + const char* timestamp = ConstMap_GetValue(props, GW_TIMESTAMP_PROPERTY); + const char* characteristic_uuid = ConstMap_GetValue(props, GW_CHARACTERISTIC_UUID_PROPERTY); + const CONSTBUFFER* buffer = Message_GetContent(message); + if (buffer != NULL && characteristic_uuid != NULL) + { + // dispatch the message based on the characteristic uuid + size_t i; + for (i = 0; i < g_dispatch_entries_length; i++) + { + if (g_ascii_strcasecmp( + characteristic_uuid, + g_dispatch_entries[i].characteristic_uuid + ) == 0) + { + g_dispatch_entries[i].message_printer( + g_dispatch_entries[i].name, + timestamp, + buffer + ); + break; + } + } + + if (i == g_dispatch_entries_length) + { + // dispatch to default printer + print_default(characteristic_uuid, timestamp, buffer); + } + } + else + { + LogError("Message is invalid. Nothing to print."); + } + } + } + else + { + LogError("Message_GetProperties for the message returned NULL"); + } + } + else + { + LogError("message is NULL"); + } +} + +void BLEPrinter_Destroy(MODULE_HANDLE module) +{ + // Nothing to do here +} + +static const MODULE_APIS Module_GetAPIS_Impl = +{ + BLEPrinter_Create, + BLEPrinter_Destroy, + BLEPrinter_Receive +}; + +const MODULE_APIS* Module_GetAPIS(void) +{ + return &Module_GetAPIS_Impl; +} + +static void print_string(const char* name, const char* timestamp, const CONSTBUFFER* buffer) +{ + printf("[%s] %s: %.*s\r\n", timestamp, name, (int)buffer->size, buffer->buffer); +} + +/** + * Code taken from: + * http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User's_Guide#Data + */ +static void sensortag_temp_convert( + uint16_t rawAmbTemp, + uint16_t rawObjTemp, + float *tAmb, + float *tObj +) +{ + const float SCALE_LSB = 0.03125; + float t; + int it; + + it = (int)((rawObjTemp) >> 2); + t = ((float)(it)) * SCALE_LSB; + *tObj = t; + + it = (int)((rawAmbTemp) >> 2); + t = (float)it; + *tAmb = t * SCALE_LSB; +} + +static void print_temperature(const char* name, const char* timestamp, const CONSTBUFFER* buffer) +{ + if (buffer->size == 4) + { + uint16_t* temps = (uint16_t *)buffer->buffer; + float ambient, object; + sensortag_temp_convert(temps[0], temps[1], &ambient, &object); + printf("[%s] %s: (%f, %f)\r\n", + timestamp, + name, + ambient, + object + ); + } +} + +static void print_default(const char* name, const char* timestamp, const CONSTBUFFER* buffer) +{ + printf("[%s] %s: ", timestamp, name); + for (size_t i = 0; i < buffer->size; ++i) + { + printf("%02X ", buffer->buffer[i]); + } + printf("\r\n"); +} \ No newline at end of file diff --git a/samples/ble_gateway_hl/src/gateway_sample.json b/samples/ble_gateway_hl/src/gateway_sample.json new file mode 100644 index 00000000..4953eb81 --- /dev/null +++ b/samples/ble_gateway_hl/src/gateway_sample.json @@ -0,0 +1,85 @@ +{ + "modules": [ + { + "module name": "IoTHub", + "module path": "<
>", + "args": { + "IoTHubName": "<>", + "IoTHubSuffix": "<>" + } + }, + { + "module name": "BLE Printer", + "module path": "<
>", + "args": null + }, + { + "module name": "mapping", + "module path": "<
>", + "args": [ + { + "macAddress": "AA:BB:CC:DD:EE:FF", + "deviceId": "<>", + "deviceKey": "<>" + } + ] + }, + { + "module name": "SensorTag", + "module path": "<
>", + "args": { + "controller_index": 0, + "device_mac_address": "<>", + "instructions": [ + { + "type": "read_once", + "characteristic_uuid": "00002A24-0000-1000-8000-00805F9B34FB" + }, + { + "type": "read_once", + "characteristic_uuid": "00002A25-0000-1000-8000-00805F9B34FB" + }, + { + "type": "read_once", + "characteristic_uuid": "00002A26-0000-1000-8000-00805F9B34FB" + }, + { + "type": "read_once", + "characteristic_uuid": "00002A27-0000-1000-8000-00805F9B34FB" + }, + { + "type": "read_once", + "characteristic_uuid": "00002A28-0000-1000-8000-00805F9B34FB" + }, + { + "type": "read_once", + "characteristic_uuid": "00002A29-0000-1000-8000-00805F9B34FB" + }, + { + "type": "write_at_init", + "characteristic_uuid": "F000AA02-0451-4000-B000-000000000000", + "data": "AQ==" + }, + { + "type": "read_periodic", + "characteristic_uuid": "F000AA01-0451-4000-B000-000000000000", + "interval_in_ms": 1000 + }, + { + "type": "write_at_exit", + "characteristic_uuid": "F000AA02-0451-4000-B000-000000000000", + "data": "AA==" + } + ] + } + }, + { + "module name": "Logger", + "module path" : "<>", + "args": + { + "filename": "<>" + } + } + ] +} \ No newline at end of file diff --git a/samples/ble_gateway_hl/src/main.c b/samples/ble_gateway_hl/src/main.c new file mode 100644 index 00000000..2fa37732 --- /dev/null +++ b/samples/ble_gateway_hl/src/main.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "azure_c_shared_utility/iot_logging.h" + +#include "gateway.h" + +static void print_usage(); +static bool validate_args(int argc, char* argv[]); +static void handle_control_c(GMainLoop* loop); +static gboolean signal_handler( + GIOChannel *channel, + GIOCondition condition, + gpointer user_data +); + +guint g_event_source_id = 0; + +int main(int argc, char*argv[]) +{ + // create glib loop and register a ctrl+c handler + GMainLoop* loop = g_main_loop_new(NULL, FALSE); + handle_control_c(loop); + + int result; + if (validate_args(argc, argv) == false) + { + result = 1; + } + else + { + char *json_path = argv[1]; + GATEWAY_HANDLE gateway = Gateway_Create_From_JSON(json_path); + if(gateway == NULL) + { + LogError("An error ocurred while creating the gateway."); + result = 1; + } + else + { + result = 0; + printf("Gateway is running.\r\n"); + + // run the glib loop + g_main_loop_run(loop); + + printf("Gateway is quitting\r\n"); + Gateway_LL_Destroy(gateway); + } + } + + return result; +} + +static bool validate_args(int argc, char* argv[]) +{ + bool result; + const size_t EXPECTED_PARAMS = 2; + + // we expect the path to the gateway JSON to be passed in + if (argc < EXPECTED_PARAMS) + { + print_usage(); + result = false; + } + else + { + result = true; + for (size_t i = 1; i < EXPECTED_PARAMS; i++) + { + char* param = argv[i]; + if (strlen(param) == 0) + { + result = false; + break; + } + } + } + + return result; +} + +static void print_usage() +{ + printf("\r\nUsage:\r\n" + " ble_gateway_hl \r\n\r\n"); +} + +static void handle_control_c(GMainLoop* loop) +{ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) + { + LogError("Failed to set signal mask"); + } + else + { + int fd = signalfd(-1, &mask, 0); + if (fd < 0) + { + LogError("Failed to create signal descriptor"); + } + else + { + GIOChannel *channel = g_io_channel_unix_new(fd); + if (channel == NULL) + { + close(fd); + LogError("Failed to create IO channel"); + } + else + { + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + g_event_source_id = g_io_add_watch( + channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + signal_handler, + loop + ); + + if (g_event_source_id == 0) + { + LogError("g_io_add_watch failed"); + } + + g_main_loop_ref(loop); + g_io_channel_unref(channel); + } + } + } +} + +static gboolean signal_handler( + GIOChannel *channel, + GIOCondition condition, + gpointer user_data +) +{ + static unsigned int terminated = 0; + struct signalfd_siginfo si; + int fd; + GMainLoop* loop = (GMainLoop*)user_data; + gboolean result; + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + LogInfo("Quitting..."); + g_main_loop_unref(loop); + g_source_remove(g_event_source_id); + g_main_loop_quit(loop); + result = FALSE; + } + else + { + fd = g_io_channel_unix_get_fd(channel); + + if (read(fd, &si, sizeof(si)) != sizeof(si)) + { + LogError("read from fd failed"); + result = FALSE; + } + else + { + switch (si.ssi_signo) { + case SIGINT: + LogInfo("Caught ctrl+c - quitting..."); + g_main_loop_unref(loop); + g_source_remove(g_event_source_id); + g_main_loop_quit(loop); + break; + case SIGTERM: + if (terminated == 0) { + LogInfo("Caught SIGTERM - quitting..."); + g_main_loop_unref(loop); + g_source_remove(g_event_source_id); + g_main_loop_quit(loop); + } + + terminated = 1; + break; + } + + result = TRUE; + } + } + + return result; +} \ No newline at end of file diff --git a/samples/hello_world/CMakeLists.txt b/samples/hello_world/CMakeLists.txt new file mode 100644 index 00000000..816e4b6f --- /dev/null +++ b/samples/hello_world/CMakeLists.txt @@ -0,0 +1,36 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) +#this is CMakeLists for hello_world sample + +set(hello_world_sources + ./src/main.c +) +if(WIN32) + set(hello_world_sources + ${hello_world_sources} + ./src/hello_world_win.json + ) + set_source_files_properties(./src/hello_world_win.json PROPERTIES HEADER_FILE_ONLY ON) +else() + set(hello_world_sources + ${hello_world_sources} + ./src/hello_world_lin.json + ) + set_source_files_properties(./src/hello_world_lin.json PROPERTIES HEADER_FILE_ONLY ON) +endif() + +set(hello_world_headers +) + +include_directories(./inc ${IOTHUB_CLIENT_INC_FOLDER}) +include_directories(${GW_INC}) +include_directories(../../modules/common) + +add_executable(hello_world_sample ${hello_world_headers} ${modules_path_file} ${hello_world_sources}) + +target_link_libraries(hello_world_sample gateway) +linkSharedUtil(hello_world_sample) + +add_sample_to_solution(hello_world_sample) \ No newline at end of file diff --git a/samples/hello_world/README.md b/samples/hello_world/README.md new file mode 100644 index 00000000..1f5ae300 --- /dev/null +++ b/samples/hello_world/README.md @@ -0,0 +1 @@ +The directions to run this sample can be found at [doc/sample_helloworld.md](../../doc/sample_helloworld.md). \ No newline at end of file diff --git a/samples/hello_world/src/hello_world_lin.json b/samples/hello_world/src/hello_world_lin.json new file mode 100644 index 00000000..fabfb84e --- /dev/null +++ b/samples/hello_world/src/hello_world_lin.json @@ -0,0 +1,15 @@ +{ + "modules" : + [ + { + "module name" : "logger_hl", + "module path" : "./modules/logger/liblogger_hl.so", + "args" : {"filename":"log.txt"} + }, + { + "module name" : "hello_world", + "module path" : "./modules/hello_world/libhello_world_hl.so", + "args" : null + } + ] +} diff --git a/samples/hello_world/src/hello_world_win.json b/samples/hello_world/src/hello_world_win.json new file mode 100644 index 00000000..0e1cdcde --- /dev/null +++ b/samples/hello_world/src/hello_world_win.json @@ -0,0 +1,15 @@ +{ + "modules" : + [ + { + "module name" : "logger_hl", + "module path" : "..\\..\\..\\modules\\logger\\Debug\\logger_hl.dll", + "args" : {"filename":"d:\\log.txt"} + }, + { + "module name" : "hello_world", + "module path" : "..\\..\\..\\modules\\hello_world\\Debug\\hello_world_hl.dll", + "args" : null + } + ] +} diff --git a/samples/hello_world/src/main.c b/samples/hello_world/src/main.c new file mode 100644 index 00000000..eea952f2 --- /dev/null +++ b/samples/hello_world/src/main.c @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#include "gateway.h" + +int main(int argc, char** argv) +{ + GATEWAY_HANDLE gateway; + if (argc != 2) + { + printf("usage: hello_world_sample configFile\n"); + printf("where configFile is the name of the file that contains the Gateway configuration\n"); + } + else + { + if ((gateway = Gateway_Create_From_JSON(argv[1])) == NULL) + { + printf("failed to create the gateway from JSON\n"); + } + else + { + printf("gateway successfully created from JSON\n"); + printf("gateway shall run until ENTER is pressed\n"); + (void)getchar(); + Gateway_LL_Destroy(gateway); + } + } + return 0; +} diff --git a/samples/simulated_device_cloud_upload/CMakeLists.txt b/samples/simulated_device_cloud_upload/CMakeLists.txt new file mode 100644 index 00000000..f3e0da41 --- /dev/null +++ b/samples/simulated_device_cloud_upload/CMakeLists.txt @@ -0,0 +1,50 @@ +#Copyright (c) Microsoft. All rights reserved. +#Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required(VERSION 2.8.11) +#this is CMakeLists for simulated_device_cloud_upload sample + +set(simulated_device_cloud_upload_sources + ./src/main.c +) + +if(WIN32) + set(simulated_device_cloud_upload_sources + ${simulated_device_cloud_upload_sources} + ./src/simulated_device_cloud_upload_win.json + ) + set_source_files_properties(./src/simulated_device_cloud_upload_win.json PROPERTIES HEADER_FILE_ONLY ON) +else() + set(simulated_device_cloud_upload_sources + ${simulated_device_cloud_upload_sources} + ./src/simulated_device_cloud_upload_lin.json + ) + set_source_files_properties(./src/simulated_device_cloud_upload_lin.json PROPERTIES HEADER_FILE_ONLY ON) +endif() + +if(run_as_a_service) + set(modules_service_c_file ./src/module_service_config_enabled.c) +else() + set(modules_service_c_file ./src/module_service_config_disabled.c) +endif() + +include_directories(./inc ${IOTHUB_CLIENT_INC_FOLDER}) +include_directories(${GW_INC}) +include_directories(../../modules/common) + +#link modules +linkModule(logger) +linkModule(identitymap) +linkModule(iothubhttp) +linkModule(simulated_device) + +add_executable(simulated_device_cloud_upload_sample ${modules_service_c_file} ${simulated_device_cloud_upload_sources}) +target_link_libraries(simulated_device_cloud_upload_sample gateway) +linkSharedUtil(simulated_device_cloud_upload_sample) +linkHttp(simulated_device_cloud_upload_sample) + +add_sample_to_solution(simulated_device_cloud_upload_sample) + +if(install_executables) + install(TARGETS simulated_device_cloud_upload_sample DESTINATION bin) +endif() \ No newline at end of file diff --git a/samples/simulated_device_cloud_upload/README.md b/samples/simulated_device_cloud_upload/README.md new file mode 100644 index 00000000..997cee31 --- /dev/null +++ b/samples/simulated_device_cloud_upload/README.md @@ -0,0 +1 @@ +The directions to run this sample can be found at [doc/sample_simple_device_cloud_upload.md](../../doc/sample_simple_device_cloud_upload.md). \ No newline at end of file diff --git a/samples/simulated_device_cloud_upload/inc/simulated_device_cloud_upload_config_service.h b/samples/simulated_device_cloud_upload/inc/simulated_device_cloud_upload_config_service.h new file mode 100644 index 00000000..fff9e31a --- /dev/null +++ b/samples/simulated_device_cloud_upload/inc/simulated_device_cloud_upload_config_service.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SIMULATED_DEVICE_CLOUD_UPLOAD_CONFIG_SERVICE_H +#define SIMULATED_DEVICE_CLOUD_UPLOAD_CONFIG_SERVICE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern int configureAsAService(void); + +extern void waitForUserInput(void); + +#ifdef __cplusplus +} +#endif + +#endif /*SIMULATED_DEVICE_CLOUD_UPLOAD_CONFIG_SERVICE_H*/ diff --git a/samples/simulated_device_cloud_upload/src/azure-iot-field-gateway-sdk.bb b/samples/simulated_device_cloud_upload/src/azure-iot-field-gateway-sdk.bb new file mode 100644 index 00000000..b855c921 --- /dev/null +++ b/samples/simulated_device_cloud_upload/src/azure-iot-field-gateway-sdk.bb @@ -0,0 +1,33 @@ +DESCRIPTION = "" +LICENSE = "MI" +SRC_URI = "file:///home/<>/azure-iot-gateway-sdk" + +LIC_FILES_CHKSUM = "file:///home/<>/azure-iot-gateway-sdk/deps/azure-iot-sdks/LICENSE;md5=4283671594edec4c13aeb073c219237a" + +PROVIDES = "azure-iot-gateway-sdk" + +DEPENDS = "glib-2.0 curl" + +EXTRA_OECMAKE = "-Dinstall_executables:BOOL=ON -Drun_as_a_service:BOOL=ON -Dskip_unittests:BOOL=ON" + +S = "${WORKDIR}/home/azure-iot-gateway-sdk" + +SYSTEMD_SERVICE_${PN} = "simulated_device_cloud_upload.service" + +FILES_${PN} = "${systemd_unitdir}/system/*" +FILES_${PN} += "${bindir}/*" +FILES_${PN} += "${libdir}/*.so" + +FILES_SOLIBSDEV = "" + +inherit pkgconfig cmake systemd + +do_install_append() { + # install service file + install -d ${D}${systemd_unitdir}/system + install -c -m 0644 ${WORKDIR}/home/azure-iot-gateway-sdk/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload.service ${D}${systemd_unitdir}/system + + install -d ${D}${bindir} + install -c -m 0644 ${WORKDIR}/home/azure-iot-gateway-sdk/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_intel_edison.json ${D}${bindir} + +} \ No newline at end of file diff --git a/samples/simulated_device_cloud_upload/src/main.c b/samples/simulated_device_cloud_upload/src/main.c new file mode 100644 index 00000000..8493224f --- /dev/null +++ b/samples/simulated_device_cloud_upload/src/main.c @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#include "simulated_device_cloud_upload_config_service.h" +#include "gateway.h" +#include "azure_c_shared_utility/iot_logging.h" +#include "azure_c_shared_utility/vector.h" + + +int main(int argc, char** argv) +{ + GATEWAY_HANDLE deviceCloudUploadGateway; + + if (argc != 2) + { + printf("usage: simulated_device_cloud_upload gatewayConfigFile\n"); + printf("gatewayConfigFile is a JSON configuration file \n"); + } + else + { + if (configureAsAService() != 0) + { + LogError("Could not configure gateway as a service."); + } + else + { + deviceCloudUploadGateway = Gateway_Create_From_JSON(argv[1]); + if (deviceCloudUploadGateway == NULL) + { + LogError("Failed to create gateway"); + } + else + { + /* Wait for user input to quit. */ + waitForUserInput(); + Gateway_LL_Destroy(deviceCloudUploadGateway); + } + } + } + return 0; +} diff --git a/samples/simulated_device_cloud_upload/src/module_service_config_disabled.c b/samples/simulated_device_cloud_upload/src/module_service_config_disabled.c new file mode 100644 index 00000000..2fc8c484 --- /dev/null +++ b/samples/simulated_device_cloud_upload/src/module_service_config_disabled.c @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +//Windows Service not yet supported. +int configureAsAService(void) +{ + return 0; +} + + +void waitForUserInput(void) +{ + (void)printf("Press return to exit the application. \r\n"); + (void)getchar(); +} diff --git a/samples/simulated_device_cloud_upload/src/module_service_config_enabled.c b/samples/simulated_device_cloud_upload/src/module_service_config_enabled.c new file mode 100644 index 00000000..44efda11 --- /dev/null +++ b/samples/simulated_device_cloud_upload/src/module_service_config_enabled.c @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "azure_c_shared_utility/iot_logging.h" +#include + +int configureAsAService(void) +{ + int returnValue; + int functionReturn; + + returnValue = chdir("/"); + + if (returnValue != 0) + { + LogError("Could not change working dir. Error Code: %d.", returnValue); + functionReturn = __LINE__; + } + else + { + returnValue = daemon(0, 0); + if (returnValue != 0) + { + LogError("Could not detach program from terminal. Error Code: %d.", returnValue); + functionReturn = __LINE__; + } + else + { + functionReturn = 0; + } + } + + return functionReturn; +} + +void waitForUserInput(void) +{ + while(1) + { + sleep(1000); //Stays here till the service is stopped. + } +} diff --git a/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload.service b/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload.service new file mode 100644 index 00000000..bd571c7a --- /dev/null +++ b/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload.service @@ -0,0 +1,12 @@ +[Unit] +Description=Azure Iot Gateway as a service. +After=systemd-hostnamed.service + +[Service] +ExecStartPre=/bin/systemctl start wpa_supplicant + +ExecStart=/usr/bin/simulated_device_cloud_upload /usr/bin/simulated_device_cloud_upload_intel_edison.json +Type=forking + +[Install] +WantedBy=multi-user.target diff --git a/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_intel_edison.json b/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_intel_edison.json new file mode 100644 index 00000000..2469d0c2 --- /dev/null +++ b/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_intel_edison.json @@ -0,0 +1,55 @@ +{ + "modules" : + [ + { + "module name" : "IoTHub", + "module path" : "libiothubhttp_hl.so", + "args" : + { + "IoTHubName" : "<>", + "IoTHubSuffix" : "<>" + } + }, + { + "module name" : "mapping", + "module path" : "libidentity_map_hl.so", + "args" : + [ + { + "macAddress" : "01:01:01:01:01:01", + "deviceId" : "<>", + "deviceKey" : "<>" + }, + { + "macAddress" : "02:02:02:02:02:02", + "deviceId" : "<>", + "deviceKey" : "<>" + } + ] + }, + { + "module name":"BLE1", + "module path" : "libsimulated_device_hl.so", + "args": + { + "macAddress" : "01:01:01:01:01:01" + } + }, + { + "module name":"BLE2", + "module path" : "libsimulated_device_hl.so", + "args": + { + "macAddress" : "02:02:02:02:02:02" + } + }, + { + "module name":"Logger", + "module path" : "liblogger_hl.so", + "args": + { + "filename":"deviceCloudUploadGatewaylog.log" + } + } + ] +} \ No newline at end of file diff --git a/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_lin.json b/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_lin.json new file mode 100644 index 00000000..87867cc1 --- /dev/null +++ b/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_lin.json @@ -0,0 +1,55 @@ +{ + "modules" : + [ + { + "module name" : "IoTHub", + "module path" : "./modules/iothubhttp/libiothubhttp_hl.so", + "args" : + { + "IoTHubName" : "<>", + "IoTHubSuffix" : "<>" + } + }, + { + "module name" : "mapping", + "module path" : "./modules/identitymap/libidentity_map_hl.so", + "args" : + [ + { + "macAddress" : "01:01:01:01:01:01", + "deviceId" : "<>", + "deviceKey" : "<>" + }, + { + "macAddress" : "02:02:02:02:02:02", + "deviceId" : "<>", + "deviceKey" : "<>" + } + ] + }, + { + "module name":"BLE1", + "module path" : "./modules/simulated_device/libsimulated_device_hl.so", + "args": + { + "macAddress" : "01:01:01:01:01:01" + } + }, + { + "module name":"BLE2", + "module path" : "./modules/simulated_device/libsimulated_device_hl.so", + "args": + { + "macAddress" : "02:02:02:02:02:02" + } + }, + { + "module name":"Logger", + "module path" : "./modules/logger/liblogger_hl.so", + "args": + { + "filename":"deviceCloudUploadGatewaylog.log" + } + } + ] +} \ No newline at end of file diff --git a/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_win.json b/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_win.json new file mode 100644 index 00000000..515f2e2d --- /dev/null +++ b/samples/simulated_device_cloud_upload/src/simulated_device_cloud_upload_win.json @@ -0,0 +1,55 @@ +{ + "modules" : + [ + { + "module name" : "IoTHub", + "module path" : "..\\..\\..\\modules\\iothubhttp\\Debug\\iothubhttp_hl.dll", + "args" : + { + "IoTHubName" : "<>", + "IoTHubSuffix" : "<>" + } + }, + { + "module name" : "mapping", + "module path" : "..\\..\\..\\modules\\identitymap\\Debug\\identity_map_hl.dll", + "args" : + [ + { + "macAddress" : "01:01:01:01:01:01", + "deviceId" : "<>", + "deviceKey" : "<>" + }, + { + "macAddress" : "02:02:02:02:02:02", + "deviceId" : "<>", + "deviceKey" : "<>" + } + ] + }, + { + "module name":"BLE1", + "module path" : "..\\..\\..\\modules\\simulated_device\\Debug\\simulated_device_hl.dll", + "args": + { + "macAddress" : "01:01:01:01:01:01" + } + }, + { + "module name":"BLE2", + "module path" : "..\\..\\..\\modules\\simulated_device\\Debug\\simulated_device_hl.dll", + "args": + { + "macAddress" : "02:02:02:02:02:02" + } + }, + { + "module name":"Logger", + "module path" : "..\\..\\..\\modules\\logger\\Debug\\logger_hl.dll", + "args": + { + "filename":"deviceCloudUploadGatewaylog.log" + } + } + ] +} \ No newline at end of file diff --git a/tools/build.cmd b/tools/build.cmd new file mode 100644 index 00000000..bf878a29 --- /dev/null +++ b/tools/build.cmd @@ -0,0 +1,110 @@ +@REM Copyright (c) Microsoft. All rights reserved. +@REM Licensed under the MIT license. See LICENSE file in the project root for full license information. + +@setlocal EnableExtensions EnableDelayedExpansion +@echo off + +set current-path=%~dp0 + +rem // remove trailing slash +set current-path=%current-path:~0,-1% + +set build-root=%current-path%\.. +rem // resolve to fully qualified path +for %%i in ("%build-root%") do set build-root=%%~fi + +rem ----------------------------------------------------------------------------- +rem -- parse script arguments +rem ----------------------------------------------------------------------------- + +rem // default build options +set build-config=Debug +set build-platform=Win32 +set CMAKE_run_e2e_tests=OFF + +:args-loop +if "%1" equ "" goto args-done +if "%1" equ "--config" goto arg-build-config +if "%1" equ "--platform" goto arg-build-platform +if "%1" equ "--run-e2e-tests" goto arg-run-e2e-tests +call :usage && exit /b 1 + +:arg-build-config +shift +if "%1" equ "" call :usage && exit /b 1 +set build-config=%1 +goto args-continue + +:arg-build-platform +shift +if "%1" equ "" call :usage && exit /b 1 +set build-platform=%1 +goto args-continue + +:arg-run-e2e-tests +set CMAKE_run_e2e_tests=ON +goto args-continue + +:args-continue +shift +goto args-loop + +:args-done + +rem ----------------------------------------------------------------------------- +rem -- build with CMAKE and run tests +rem ----------------------------------------------------------------------------- + +rem this is setting the cmake path in a quoted way +set cmake-root="%build-root%"\build + +rem get the size of cmake path +set cmake-root-no-quotes=%build-root%\build +set size=0 +:loop +if defined cmake-root-no-quotes ( + rem chop down one character + set cmake-root-no-quotes=%cmake-root-no-quotes:~1% + rem add it to the size + set /A size += 1 + rem go to begin + goto loop +) + +rem echo %build-root%\build (%cmake-root%)is %size% characters long! +if %size% GTR 49 ( + @echo build.cmd cannot continue because the path %build-root%\build is too long! + @echo try placing the repo in a shorter path. the path size can be at most 49 characters + exit /b 1 +) + +rmdir /s/q %cmake-root% + +mkdir %cmake-root% +rem no error checking + +pushd %cmake-root% +cmake -Drun_e2e_tests:BOOL=%CMAKE_run_e2e_tests% "%build-root%" "%build-root%" +if not %errorlevel%==0 exit /b %errorlevel% + +msbuild /m /p:Configuration="%build-config%" /p:Platform="%build-platform%" azure_iot_gateway_sdk.sln +if not %errorlevel%==0 exit /b %errorlevel% + +ctest -C "debug" -V +if not %errorlevel%==0 exit /b %errorlevel% + +popd +goto :eof + +rem ----------------------------------------------------------------------------- +rem -- subroutines +rem ----------------------------------------------------------------------------- + +:usage +echo build.cmd [options] +echo options: +echo --config ^ [Debug] build configuration (e.g. Debug, Release) +echo --platform ^ [Win32] build platform (e.g. Win32, x64, ...) +echo --run-e2e-tests run end-to-end tests +goto :eof + diff --git a/tools/build.sh b/tools/build.sh new file mode 100644 index 00000000..c752987e --- /dev/null +++ b/tools/build.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# Copyright (c) Microsoft. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. + +set -e + +build_clean= +build_root=$(cd "$(dirname "$0")/.." && pwd) +log_dir=$build_root +run_unit_tests=ON +run_e2e_tests=ON +run_valgrind=0 +cd "$build_root" +usage () +{ + echo "build.sh [options]" + echo "options" + echo " -x, --xtrace print a trace of each command" + echo " -c, --clean remove artifacts from previous build be +fore building" + echo " -cl, --compileoption specify a compile option to be passed to gcc" + echo " Example: -cl -O1 -cl ..." + echo "-rv, --run_valgrind will execute ctest with valgrind" + echo " --skip-e2e-tests skip the running of end-to-end tests (e2e tests are run by default)" + exit 1 +} + +process_args () +{ + build_clean=0 + save_next_arg=0 + extracloptions=" " + + for arg in $* + do + if [ $save_next_arg == 1 ] + then + # save arg to pass to gcc + extracloptions="$extracloptions $arg" + save_next_arg=0 + else + case "$arg" in + "-x" | "--xtrace" ) set -x;; + "-c" | "--clean" ) build_clean=1;; + "--skip-e2e-tests" ) run_e2e_tests=OFF;; + "-cl" | "--compileoption" ) save_next_arg=1;; + "-rv" | "--run_valgrind" ) run_valgrind=1;; + * ) usage;; + esac + fi + done +} + +process_args $* + +cmake_root="$build_root"/build +rm -r -f "$cmake_root" +mkdir -p "$cmake_root" +pushd "$cmake_root" +cmake -DCMAKE_BUILD_TYPE=Debug -Drun_e2e_tests:BOOL=$run_e2e_tests -Drun_valgrind:BOOL=$run_valgrind "$build_root" +make --jobs=$(nproc) + + + +if [[ $run_valgrind == 1 ]] ; +then + #use doctored (-DPURIFY no-asm) openssl + export LD_LIBRARY_PATH=/usr/local/ssl/lib + ctest -j $(nproc) --output-on-failure + export LD_LIBRARY_PATH= +else + ctest -j $(nproc) -C "Debug" --output-on-failure +fi + +popd + +[ $? -eq 0 ] || exit $? + diff --git a/tools/docs/Doxyfile b/tools/docs/Doxyfile new file mode 100644 index 00000000..084d88ed --- /dev/null +++ b/tools/docs/Doxyfile @@ -0,0 +1,2438 @@ +# Doxyfile 1.8.11 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Microsoft Azure IoT Gateway SDK" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = 1.0.0-beta.1 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "The Microsoft Azure IoT Gateway SDK contains the infrastructure and modules to create IoT gateway solutions." + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = ../../doc/api_reference/c/ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = NO + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = mainpage.dox \ + ../../core/inc/gateway.h \ + ../../core/inc/gateway_ll.h \ + ../../core/inc/message.h \ + ../../core/inc/message_bus.h \ + ../../core/inc/module.h \ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, +# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* +# +# We prevent extra DEFINE_ENUM symbols from being generated in the docs. +# Other unused symbols are ignored as well. + +EXCLUDE_SYMBOLS = *_VALUES \ + MESSAGE_BUS_RESULTStrings \ + MESSAGE_BUS_RESULT_FromString \ + BUS_offsetof_quit_worker \ + MODULE_EXPORT + + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse-libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML 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: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_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. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# 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). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /