├── .gitignore ├── AUTHORS ├── BUILD ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── WORKSPACE ├── statechart ├── BUILD ├── example │ ├── BUILD │ ├── microwave.proto │ ├── microwave_example_main.cc │ └── microwave_statechart.svg ├── internal │ ├── BUILD │ ├── datamodel.cc │ ├── datamodel.h │ ├── event_dispatcher.cc │ ├── event_dispatcher.h │ ├── event_dispatcher_test.cc │ ├── executor.cc │ ├── executor.h │ ├── executor_test.cc │ ├── function_dispatcher.h │ ├── function_dispatcher_builtin.cc │ ├── function_dispatcher_builtin.h │ ├── function_dispatcher_builtin_test.cc │ ├── function_dispatcher_impl.cc │ ├── function_dispatcher_impl.h │ ├── function_dispatcher_impl_test.cc │ ├── json_value_coder.h │ ├── json_value_coder_test.cc │ ├── light_weight_datamodel.cc │ ├── light_weight_datamodel.h │ ├── light_weight_datamodel_test.cc │ ├── model.cc │ ├── model.h │ ├── model │ │ ├── BUILD │ │ ├── assign.cc │ │ ├── assign.h │ │ ├── assign_test.cc │ │ ├── data.cc │ │ ├── data.h │ │ ├── data_test.cc │ │ ├── executable_block.h │ │ ├── executable_block_test.cc │ │ ├── executable_content.h │ │ ├── for_each.cc │ │ ├── for_each.h │ │ ├── for_each_test.cc │ │ ├── if.cc │ │ ├── if.h │ │ ├── if_test.cc │ │ ├── log.cc │ │ ├── log.h │ │ ├── log_test.cc │ │ ├── model.h │ │ ├── model_element.cc │ │ ├── model_element.h │ │ ├── raise.cc │ │ ├── raise.h │ │ ├── raise_test.cc │ │ ├── send.cc │ │ ├── send.h │ │ ├── send_test.cc │ │ ├── state.cc │ │ ├── state.h │ │ ├── str_or_expr.cc │ │ ├── str_or_expr.h │ │ ├── str_or_expr_test.cc │ │ ├── transition.cc │ │ ├── transition.h │ │ └── transition_test.cc │ ├── model_builder.cc │ ├── model_builder.h │ ├── model_builder_test.cc │ ├── model_impl.cc │ ├── model_impl.h │ ├── model_impl_test.cc │ ├── model_test.cc │ ├── runtime.cc │ ├── runtime.h │ ├── runtime_impl.cc │ ├── runtime_impl.h │ ├── runtime_impl_test.cc │ ├── state_machine_impl.cc │ ├── state_machine_impl.h │ ├── state_machine_logger.cc │ ├── state_machine_logger.h │ ├── testing │ │ ├── BUILD │ │ ├── delegating_mock_executor.h │ │ ├── json_value_coder_test.proto │ │ ├── mock_datamodel.h │ │ ├── mock_event_dispatcher.h │ │ ├── mock_executable_content.h │ │ ├── mock_function_dispatcher.h │ │ ├── mock_model.h │ │ ├── mock_runtime.h │ │ ├── mock_state.h │ │ ├── mock_transition.h │ │ ├── state_chart_builder.h │ │ └── state_chart_builder_test.cc │ ├── utility.cc │ ├── utility.h │ └── utility_test.cc ├── json_utils.cc ├── json_utils.h ├── json_utils_test.cc ├── logging.h ├── logging_test.cc ├── platform │ ├── BUILD │ ├── logging.h │ ├── map_util.h │ ├── protobuf.h │ ├── str_util.cc │ ├── str_util.h │ ├── test_util.h │ └── types.h ├── proto │ ├── BUILD │ ├── state_chart.proto │ └── state_machine_context.proto ├── state_machine.cc ├── state_machine.h ├── state_machine_factory.cc ├── state_machine_factory.h ├── state_machine_factory_test.cc ├── state_machine_listener.h ├── state_machine_test.cc └── testing │ ├── BUILD │ ├── mock_state_machine.h │ └── state_machine_test.proto └── third_party ├── BUILD └── jsoncpp.BUILD /.gitignore: -------------------------------------------------------------------------------- 1 | bazel-* 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the list of [Project Name] authors for copyright purposes. 2 | # 3 | # This does not necessarily list everyone who has contributed code, since in 4 | # some cases, their employer may be the copyright holder. To see the full list 5 | # of contributors, see the revision history in source control. 6 | 7 | Google LLC. 8 | Qiangfeng Peter Lau 9 | Sudeep Gandhe 10 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/statechart/d283cefab5a051a91c21be46d97bc18407fd7628/BUILD -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution, 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google.com/conduct/). 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ StateChart Library 2 | 3 | StateCharts [(Harel, 1987)](ref1) is a visual formalism for declarative 4 | description of complex interactive systems. 5 | It extends the conventional state machine formalism with Hierarchy, Concurrency 6 | and Communication. 7 | 8 | [SCXML](https://www.w3.org/TR/scxml/) is a W3C standard based on StateCharts. 9 | This is a C++ library that implements the SCXML specification. Instead of XML, a 10 | StateChart is represented as a StateChart protobuf. 11 | 12 | ## Features 13 | Many features from the SCXML standard have been implemented. 14 | 15 | * Composite State 16 | * Parallel State 17 | * Datamodel 18 | * Transitions (Conditional, Eventless) 19 | * Executable Statements (Raise, Log, Assign, If, ForEach, Send) 20 | * Serialize/Deserialize the state machine 21 | 22 | ## TODO 23 | 24 | * History 25 | 26 | ## Example 27 | Here's an example of a StateChart that describes a Microwave. 28 | 29 | ![Microwave StateChart](statechart/example/microwave_statechart.svg "Microwave StateChart") 30 | 31 | You can see [//statechart/example/microwave_example_main.cc](statechart/example/microwave_example_main.cc) 32 | for details on how to specify such a StateChart as a StateChart proto and how to use it in code. 33 | 34 | ## Usage 35 | 36 | To build the library you'll need bazel. You can download and install it from [here](https://www.bazel.build/). 37 | 38 | ``` 39 | # Download/Clone the repo. 40 | git clone https://github.com/google/statechart.git 41 | cd statechart 42 | 43 | # Build the library 44 | bazel build //statechart/... 45 | 46 | # Run unit tests 47 | bazel test //statechart/... 48 | 49 | # Run the Microwave example 50 | bazel run //statechart/example:microwave_example_main -- --alsologtostderr 51 | ``` 52 | 53 | ## Disclaimer 54 | 55 | This is not an officially supported Google product. 56 | 57 | ## References 58 | 59 | * David Harel, 1987, Statecharts: a visual formalism for complex systems, 60 | Science of Computer Programming. 61 | [Link](https://www.sciencedirect.com/science/article/pii/0167642387900359) 62 | * State Chart XML (SCXML): State Machine Notation for Control Abstraction. 63 | [Link](https://www.w3.org/TR/scxml/) 64 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "com_google_statechart") 2 | 3 | # Using protobuf version 3.5.1 4 | http_archive( 5 | name = "com_google_protobuf", 6 | strip_prefix = "protobuf-3.5.1", 7 | urls = ["https://github.com/google/protobuf/archive/v3.5.1.tar.gz"], 8 | ) 9 | 10 | http_archive( 11 | name = "com_google_protobuf_cc", 12 | strip_prefix = "protobuf-3.5.1", 13 | urls = ["https://github.com/google/protobuf/archive/v3.5.1.tar.gz"], 14 | ) 15 | 16 | # GoogleTest/GoogleMock framework. Used by most unit-tests. 17 | http_archive( 18 | name = "com_google_googletest", 19 | urls = ["https://github.com/google/googletest/archive/master.zip"], 20 | strip_prefix = "googletest-master", 21 | ) 22 | 23 | # We depend on Abseil. 24 | http_archive( 25 | name = "com_google_absl", 26 | strip_prefix = "abseil-cpp-master", 27 | urls = ["https://github.com/abseil/abseil-cpp/archive/master.zip"], 28 | ) 29 | 30 | # Abseil depends on CCTZ (Time-zone framework). 31 | http_archive( 32 | name = "com_googlesource_code_cctz", 33 | strip_prefix = "cctz-master", 34 | urls = ["https://github.com/google/cctz/archive/master.zip"], 35 | ) 36 | 37 | http_archive( 38 | name = "com_googlesource_code_re2", 39 | urls = ["https://github.com/google/re2/archive/master.zip"], 40 | strip_prefix = "re2-master", 41 | ) 42 | 43 | new_http_archive( 44 | name = "jsoncpp_git", 45 | urls = ["https://github.com/open-source-parsers/jsoncpp/archive/1.7.6.tar.gz"], 46 | strip_prefix = "jsoncpp-1.7.6", 47 | build_file = "//third_party:jsoncpp.BUILD", 48 | ) 49 | 50 | http_archive( 51 | name = "com_github_gflags_gflags", 52 | urls = [ "https://github.com/gflags/gflags/archive/master.zip" ], 53 | strip_prefix = "gflags-master" 54 | ) 55 | # 56 | http_archive( 57 | name = "com_google_glog", 58 | urls = [ "https://github.com/google/glog/archive/master.zip" ], 59 | strip_prefix = "glog-master" 60 | ) 61 | 62 | # gflags needed by glog 63 | # http_archive( 64 | # name = "com_github_gflags_gflags", 65 | # sha256 = "6e16c8bc91b1310a44f3965e616383dbda48f83e8c1eaa2370a215057b00cabe", 66 | # strip_prefix = "gflags-77592648e3f3be87d6c7123eb81cbad75f9aef5a", 67 | # urls = [ 68 | # "https://mirror.bazel.build/github.com/gflags/gflags/archive/77592648e3f3be87d6c7123eb81cbad75f9aef5a.tar.gz", 69 | # "https://github.com/gflags/gflags/archive/77592648e3f3be87d6c7123eb81cbad75f9aef5a.tar.gz", 70 | # ], 71 | # ) 72 | 73 | # glog 74 | # http_archive( 75 | # name = "com_google_glog", 76 | # sha256 = "1ee310e5d0a19b9d584a855000434bb724aa744745d5b8ab1855c85bff8a8e21", 77 | # strip_prefix = "glog-028d37889a1e80e8a07da1b8945ac706259e5fd8", 78 | # urls = [ 79 | # "https://mirror.bazel.build/github.com/google/glog/archive/028d37889a1e80e8a07da1b8945ac706259e5fd8.tar.gz", 80 | # "https://github.com/google/glog/archive/028d37889a1e80e8a07da1b8945ac706259e5fd8.tar.gz", 81 | # ], 82 | # ) -------------------------------------------------------------------------------- /statechart/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The StateChart Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Description: 16 | # Libraries for a generic StateChart executor based on SCXML standard. 17 | 18 | package(default_visibility = ["//visibility:public"]) 19 | 20 | cc_library( 21 | name = "state_machine", 22 | srcs = ["state_machine.cc"], 23 | hdrs = ["state_machine.h"], 24 | deps = [ 25 | ":logging", 26 | "//statechart/internal:datamodel", 27 | "//statechart/internal:model", 28 | "//statechart/internal:runtime", 29 | "//statechart/platform:protobuf", 30 | "//statechart/proto:state_machine_context_cc_proto", 31 | ], 32 | ) 33 | # TODO(srgandhe): Fix this test. 34 | #cc_test( 35 | # name = "state_machine_test", 36 | # size = "small", 37 | # srcs = ["state_machine_test.cc"], 38 | # deps = [ 39 | # ":state_machine", 40 | # "//statechart/internal/testing:mock_datamodel", 41 | # "//statechart/internal/testing:mock_runtime", 42 | # "//statechart/platform:test_util", 43 | # "//statechart/proto:state_machine_context_cc_proto", 44 | # "//statechart/testing:mock_state_machine", 45 | # "//statechart/testing:state_machine_test_cc_proto", 46 | # "@com_google_googletest//:gtest_main", 47 | # ], 48 | #) 49 | 50 | cc_library( 51 | name = "state_machine_factory", 52 | srcs = ["state_machine_factory.cc"], 53 | hdrs = ["state_machine_factory.h"], 54 | deps = [ 55 | ":logging", 56 | ":state_machine", 57 | ":state_machine_listener", 58 | "//statechart/internal:datamodel", 59 | "//statechart/internal:executor", 60 | "//statechart/internal:function_dispatcher", 61 | "//statechart/internal:light_weight_datamodel", 62 | "//statechart/internal:model", 63 | "//statechart/internal:model_builder", 64 | "//statechart/internal:model_impl", 65 | "//statechart/internal:runtime", 66 | "//statechart/internal:runtime_impl", 67 | "//statechart/internal:state_machine_impl", 68 | "//statechart/internal:state_machine_logger", 69 | "//statechart/platform:map_util", 70 | "//statechart/proto:state_chart_cc_proto", 71 | "//statechart/proto:state_machine_context_cc_proto", 72 | "@com_google_absl//absl/memory", 73 | "@com_google_absl//absl/strings", 74 | ], 75 | ) 76 | 77 | cc_test( 78 | name = "state_machine_factory_test", 79 | srcs = ["state_machine_factory_test.cc"], 80 | deps = [ 81 | ":state_machine", 82 | ":state_machine_factory", 83 | "//statechart/internal:function_dispatcher", 84 | "//statechart/internal:model", 85 | "//statechart/internal:runtime", 86 | "//statechart/internal/model", 87 | "//statechart/internal/testing:mock_function_dispatcher", 88 | "//statechart/internal/testing:state_chart_builder", 89 | "//statechart/platform:test_util", 90 | "//statechart/proto:state_chart_cc_proto", 91 | "@com_google_googletest//:gtest_main", 92 | ], 93 | ) 94 | 95 | cc_library( 96 | name = "state_machine_listener", 97 | hdrs = ["state_machine_listener.h"], 98 | deps = ["//statechart/platform:types"], 99 | ) 100 | 101 | cc_library( 102 | name = "logging", 103 | hdrs = ["logging.h"], 104 | deps = [ 105 | "//statechart/platform:logging", 106 | "@com_google_glog//:glog", 107 | ], 108 | ) 109 | 110 | cc_test( 111 | name = "logging_test", 112 | srcs = ["logging_test.cc"], 113 | deps = [ 114 | ":logging", 115 | "@com_google_googletest//:gtest_main", 116 | ], 117 | ) 118 | 119 | cc_library( 120 | name = "json_utils", 121 | srcs = ["json_utils.cc"], 122 | hdrs = ["json_utils.h"], 123 | deps = [ 124 | "//statechart/platform:map_util", 125 | "//statechart/platform:protobuf", 126 | "//statechart/platform:types", 127 | "//statechart/proto:state_machine_context_cc_proto", 128 | "@com_google_absl//absl/memory", 129 | "@com_google_absl//absl/strings", 130 | "@jsoncpp_git//:jsoncpp", 131 | ], 132 | ) 133 | 134 | cc_test( 135 | name = "json_utils_test", 136 | srcs = ["json_utils_test.cc"], 137 | deps = [ 138 | ":json_utils", 139 | "//statechart/proto:state_machine_context_cc_proto", 140 | "@com_google_googletest//:gtest_main", 141 | ], 142 | ) 143 | -------------------------------------------------------------------------------- /statechart/example/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The StateChart Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | package(default_visibility = ["//visibility:public"]) 16 | 17 | cc_binary( 18 | name = "microwave_example_main", 19 | srcs = ["microwave_example_main.cc"], 20 | deps = [ 21 | ":microwave_cc_proto", 22 | "//statechart:state_machine", 23 | "//statechart:state_machine_factory", 24 | "//statechart/internal:function_dispatcher_impl", 25 | "//statechart/platform:protobuf", 26 | "//statechart/proto:state_chart_cc_proto", 27 | "//statechart/proto:state_machine_context_cc_proto", 28 | "@com_github_gflags_gflags//:gflags", 29 | "@com_google_absl//absl/memory", 30 | "@com_google_absl//absl/strings", 31 | "@com_google_glog//:glog", 32 | ], 33 | ) 34 | 35 | proto_library( 36 | name = "microwave_proto", 37 | srcs = ["microwave.proto"], 38 | ) 39 | 40 | cc_proto_library( 41 | name = "microwave_cc_proto", 42 | deps = [":microwave_proto"], 43 | ) 44 | -------------------------------------------------------------------------------- /statechart/example/microwave.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | package example.microwave; 4 | 5 | message MicrowaveState { 6 | enum Light { 7 | UNKNOWN = 0; 8 | OFF = 1; 9 | ON = 2; 10 | } 11 | optional Light light = 1; 12 | 13 | // The amount of cooking time left. 14 | optional int32 cooking_duration_sec = 2; 15 | } 16 | 17 | message MicrowavePayload { 18 | optional int32 duration_sec = 1; 19 | } 20 | -------------------------------------------------------------------------------- /statechart/internal/datamodel.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/datamodel.h" 16 | 17 | namespace state_chart { 18 | Datamodel::~Datamodel() {} 19 | } // namespace state_chart 20 | -------------------------------------------------------------------------------- /statechart/internal/event_dispatcher.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/event_dispatcher.h" 16 | 17 | #include "statechart/internal/runtime.h" 18 | #include "statechart/state_machine_listener.h" 19 | 20 | #define FOREACH_LISTENER(what) for (const auto& listener : listeners_) { \ 21 | listener->what; \ 22 | } 23 | 24 | namespace state_chart { 25 | 26 | // virtual 27 | EventDispatcher::~EventDispatcher() {} 28 | 29 | void EventDispatcher::AddListener(StateMachineListener* listener) { 30 | listeners_.push_back(listener); 31 | } 32 | 33 | // virtual 34 | void EventDispatcher::NotifyStateEntered(const Runtime* runtime, 35 | const model::State* state) { 36 | FOREACH_LISTENER(OnStateEntered(runtime, state)); 37 | } 38 | 39 | // virtual 40 | void EventDispatcher::NotifyStateExited(const Runtime* runtime, 41 | const model::State* state) { 42 | FOREACH_LISTENER(OnStateExited(runtime, state)); 43 | } 44 | 45 | // virtual 46 | void EventDispatcher::NotifyTransitionFollowed( 47 | const Runtime* runtime, const model::Transition* transition) { 48 | FOREACH_LISTENER(OnTransitionFollowed(runtime, transition)); 49 | } 50 | 51 | // virtual 52 | void EventDispatcher::NotifySendEvent(const Runtime* runtime, 53 | const string& event, const string& target, 54 | const string& type, const string& id, 55 | const string& data) { 56 | FOREACH_LISTENER(OnSendEvent(runtime, event, target, type, id, data)); 57 | } 58 | 59 | } // namespace state_chart 60 | -------------------------------------------------------------------------------- /statechart/internal/event_dispatcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_EVENT_DISPATCHER_H_ 18 | #define STATE_CHART_INTERNAL_EVENT_DISPATCHER_H_ 19 | 20 | #include 21 | #include 22 | 23 | #include "statechart/platform/types.h" 24 | 25 | namespace state_chart { 26 | 27 | class Runtime; 28 | class StateMachineListener; 29 | 30 | namespace model { 31 | class State; 32 | class Transition; 33 | } // namespace model 34 | 35 | // This class is responsible for collecting listeners for a specific instance of 36 | // a state machine, and dispatching events when they occur. The state machine 37 | // system invokes the various Notify*() methods, which are in turn passed to the 38 | // equivalent On*() methods in the listeners. 39 | class EventDispatcher { 40 | public: 41 | virtual ~EventDispatcher(); 42 | 43 | // Does not take ownership of 'listener'. 44 | void AddListener(StateMachineListener* listener); 45 | 46 | virtual void NotifyStateEntered(const Runtime* runtime, 47 | const model::State* state); 48 | 49 | virtual void NotifyStateExited(const Runtime* runtime, 50 | const model::State* state); 51 | 52 | virtual void NotifyTransitionFollowed(const Runtime* runtime, 53 | const model::Transition* transition); 54 | 55 | virtual void NotifySendEvent(const Runtime* runtime, const string& event, 56 | const string& target, const string& type, 57 | const string& id, const string& data); 58 | 59 | private: 60 | std::vector listeners_; 61 | }; 62 | 63 | } // namespace state_chart 64 | 65 | #endif // STATE_CHART_INTERNAL_EVENT_DISPATCHER_H_ 66 | -------------------------------------------------------------------------------- /statechart/internal/event_dispatcher_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/event_dispatcher.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "statechart/internal/testing/mock_runtime.h" 21 | #include "statechart/internal/testing/mock_state.h" 22 | #include "statechart/state_machine_listener.h" 23 | 24 | namespace state_chart { 25 | namespace { 26 | 27 | class MockListener : public StateMachineListener { 28 | public: 29 | MOCK_METHOD2(OnStateEntered, void(const Runtime*, const model::State*)); 30 | MOCK_METHOD2(OnStateExited, void(const Runtime*, const model::State*)); 31 | MOCK_METHOD2(OnTransitionFollowed, 32 | void(const Runtime*, const model::Transition*)); 33 | MOCK_METHOD6(OnSendEvent, void(const Runtime*, const string&, const string&, 34 | const string&, const string&, const string&)); 35 | }; 36 | 37 | class EventDispatcherTest : public ::testing::Test { 38 | protected: 39 | EventDispatcherTest() { dispatcher_.AddListener(&listener_); } 40 | 41 | MockRuntime runtime_; 42 | testing::StrictMock listener_; 43 | EventDispatcher dispatcher_; 44 | }; 45 | 46 | TEST_F(EventDispatcherTest, OnStateEntered) { 47 | MockState state("A"); 48 | EXPECT_CALL(listener_, 49 | OnStateEntered(&runtime_, &state)); 50 | dispatcher_.NotifyStateEntered(&runtime_, &state); 51 | } 52 | 53 | TEST_F(EventDispatcherTest, OnStateExited) { 54 | MockState state("A"); 55 | EXPECT_CALL(listener_, 56 | OnStateExited(&runtime_, &state)); 57 | dispatcher_.NotifyStateExited(&runtime_, &state); 58 | } 59 | 60 | TEST_F(EventDispatcherTest, OnSendEvent) { 61 | EXPECT_CALL(listener_, 62 | OnSendEvent(&runtime_, "event", "target", "type", "id", "data")); 63 | 64 | dispatcher_.NotifySendEvent(&runtime_, "event", "target", "type", "id", 65 | "data"); 66 | } 67 | 68 | } // namespace 69 | } // namespace state_chart 70 | -------------------------------------------------------------------------------- /statechart/internal/executor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // The Executor implements most of the SCXML interpretation pseudo-code. 18 | // See: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation 19 | #ifndef STATE_CHART_INTERNAL_EXECUTOR_H_ 20 | #define STATE_CHART_INTERNAL_EXECUTOR_H_ 21 | 22 | #include 23 | #include 24 | 25 | #include "statechart/platform/types.h" 26 | 27 | namespace state_chart { 28 | class Model; 29 | class Runtime; 30 | namespace model { 31 | class Transition; 32 | } // namespace model 33 | } // namespace state_chart 34 | 35 | namespace state_chart { 36 | 37 | // This class executes the StateChart with the same semantics as the algorithm 38 | // for SCXML interpretation. An Executor instance may be shared for various 39 | // instances of Model and Runtime. In general, StateMachine instances share the 40 | // same Executor instance, may (or may not) share the same Model, and will own 41 | // an instance of Runtime. The methods on Executor reflects this by allowing 42 | // different Model and Runtime instances to be passed in as parameters. 43 | // These input parameters should be non-null. If anyone of them is nullptr no 44 | // action is performed. 45 | class Executor { 46 | public: 47 | Executor() = default; 48 | Executor(const Executor&) = delete; 49 | Executor& operator=(const Executor&) = delete; 50 | virtual ~Executor() = default; 51 | 52 | // Start the execution of the machine by entering the initial state 53 | // and executing until the state machine reaches a stable state. A stable 54 | // state is one where there are no transitions and no internal events that 55 | // can be processed. 56 | virtual void Start(const Model* model, Runtime* runtime) const; 57 | 58 | // Notify the Executor of an external event. Executes transitions based on 59 | // this event until the state machine reaches a stable state. 60 | // This corresponds to a 'macro step' in the SCXML algorithm specification. 61 | // Parameter payload may be an empty string (i.e. no payload). 62 | virtual void SendEvent(const Model* model, Runtime* runtime, 63 | const string& event, const string& payload) const; 64 | 65 | protected: 66 | // Handles all internal events until the state machine reaches a stable state. 67 | // This corresponds to mainEventLoop() procedure above from lines 6 to 35. 68 | // Input params 'model' & 'runtime' must be non-null. 69 | virtual void ExecuteUntilStable(const Model* model, Runtime* runtime) const; 70 | 71 | // Handles a single external event. This corresponds to the mainEventLoop() 72 | // procedure above from lines 40 to 53. 73 | // Parameter payload may be an empty string (i.e. no payload). 74 | // Input params 'model' & 'runtime' must be non-null. 75 | virtual void ProcessExternalEvent(const Model* model, Runtime* runtime, 76 | const string& event, 77 | const string& payload) const; 78 | 79 | // Execute a microstep. This takes a list of transitions, exits all their 80 | // source states, runs their executable blocks and finally enters all the 81 | // target states. 82 | // Input params 'model' & 'runtime' must be non-null. 83 | virtual void MicroStep( 84 | const Model* model, Runtime* runtime, 85 | const std::vector& transitions) const; 86 | 87 | // Enters the target states of the transitions, calling their 88 | // executable blocks. 89 | // Input params 'model' & 'runtime' must be non-null. 90 | virtual void EnterStates( 91 | const Model* model, Runtime* runtime, 92 | const std::vector& transitions) const; 93 | 94 | // Exits the source states of the transitions, calling executable 95 | // blocks. 96 | // Input params 'model' & 'runtime' must be non-null. 97 | virtual void ExitStates( 98 | const Model* model, Runtime* runtime, 99 | const std::vector& transitions) const; 100 | 101 | // Assigns event data to the '_event' variable in the runtime's datamodel. 102 | // Parameters: 103 | // event The event name. 104 | // payload The event data. 105 | // 106 | // Input params 'model' & 'runtime' must be non-null. 107 | virtual void AssignEventData(Runtime* runtime, const string& event, 108 | const string& payload) const; 109 | 110 | // Shutdown the state machine by exiting all states in the correct order. 111 | // Input params 'model' & 'runtime' must be non-null. 112 | virtual void Shutdown(const Model* model, Runtime* runtime) const; 113 | }; 114 | 115 | } // namespace state_chart 116 | 117 | #endif // STATE_CHART_INTERNAL_EXECUTOR_H_ 118 | -------------------------------------------------------------------------------- /statechart/internal/function_dispatcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_FUNCTION_DISPATCHER_H_ 18 | #define STATE_CHART_INTERNAL_FUNCTION_DISPATCHER_H_ 19 | 20 | #include 21 | #include 22 | 23 | #include "statechart/platform/types.h" 24 | 25 | namespace Json { class Value; } 26 | 27 | namespace state_chart { 28 | 29 | // This base class executes C++ functions with a given function name and 30 | // arguments. 31 | class FunctionDispatcher { 32 | public: 33 | FunctionDispatcher() = default; 34 | FunctionDispatcher(const FunctionDispatcher&) = delete; 35 | FunctionDispatcher& operator=(const FunctionDispatcher&) = delete; 36 | virtual ~FunctionDispatcher() = default; 37 | 38 | // Returns whether a function identified by the 'function_name' is registered 39 | // with this function dispatcher. 40 | virtual bool HasFunction(const string& function_name) const = 0; 41 | 42 | // Executes a function identified by the 'function_name' using the 'inputs' 43 | // arguments and sets the output to '*return_value'. 44 | // Returns true on successful function execution. 45 | virtual bool Execute(const string& function_name, 46 | const std::vector& inputs, 47 | Json::Value* return_value) = 0; 48 | }; 49 | 50 | } // namespace state_chart 51 | 52 | #endif // STATE_CHART_INTERNAL_FUNCTION_DISPATCHER_H_ 53 | -------------------------------------------------------------------------------- /statechart/internal/function_dispatcher_builtin.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/function_dispatcher_builtin.h" 16 | 17 | #include "include/json/json.h" 18 | 19 | namespace state_chart { 20 | namespace builtin { 21 | 22 | bool ContainsKey(const Json::Value& value, const string& field_name) { 23 | return value.isMember(field_name); 24 | } 25 | 26 | int FindFirstWithKeyValue(const Json::Value& array, const string& key, 27 | const Json::Value& value) { 28 | if (array.isArray()) { 29 | for (unsigned int i = 0; i < array.size(); ++i) { 30 | if (array[i].isMember(key) && (array[i][key] == value)) { 31 | return i; 32 | } 33 | } 34 | } 35 | return -1; 36 | } 37 | 38 | } // namespace builtin 39 | } // namespace state_chart 40 | -------------------------------------------------------------------------------- /statechart/internal/function_dispatcher_builtin.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // A set of utility function for manipulating datamodel objects. 18 | // These are builtin into every instance of FunctionDispatcher. 19 | #ifndef STATE_CHART_INTERNAL_FUNCTION_DISPATCHER_BUILTIN_H_ 20 | #define STATE_CHART_INTERNAL_FUNCTION_DISPATCHER_BUILTIN_H_ 21 | 22 | #include 23 | 24 | #include "statechart/platform/types.h" 25 | 26 | namespace Json { class Value; } 27 | 28 | namespace state_chart { 29 | namespace builtin { 30 | 31 | // Checks whether a particular field_name exists in a Json value. 32 | bool ContainsKey(const Json::Value& value, const string& field_name); 33 | 34 | // Finds the index of the first object in a 'array' of objects where the 35 | // object has a specified pair. 36 | // Returns -1 if no such matching object is found. 37 | // Returns -1 if the input array is not a Json array. 38 | // E.g., 39 | // if array = [ { "K1" : "V1" } , 40 | // { "K2" : "V2", "foo" : "bar" }, 41 | // { "K2" : "V2"} ] then, 42 | // EXPECT_EQ(0, FindFirstWithKeyValue(array, "K1", "V1")) 43 | // EXPECT_EQ(-1, FindFirstWithKeyValue(array, "K1", "")) 44 | // EXPECT_EQ(1, FindFirstWithKeyValue(array, "K2", "V2")) 45 | int FindFirstWithKeyValue(const Json::Value& array, const string& key, 46 | const Json::Value& value); 47 | 48 | } // namespace builtin 49 | } // namespace state_chart 50 | 51 | #endif // STATE_CHART_INTERNAL_FUNCTION_DISPATCHER_BUILTIN_H_ 52 | -------------------------------------------------------------------------------- /statechart/internal/function_dispatcher_builtin_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/function_dispatcher_builtin.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "statechart/platform/types.h" 22 | #include "include/json/json.h" 23 | 24 | namespace state_chart { 25 | namespace builtin { 26 | namespace { 27 | 28 | TEST(FunctionDispatcherBuiltins, ContainsKey) { 29 | Json::Reader reader; 30 | Json::Value value; 31 | CHECK(reader.parse(R"({ "K3" : { "lower" : "l" } } )", value)); 32 | EXPECT_FALSE(ContainsKey(value, "K")); 33 | EXPECT_TRUE(ContainsKey(value, "K3")); 34 | // Specifying a path as key does not work. 35 | EXPECT_FALSE(ContainsKey(value, "K3.lower")); 36 | } 37 | 38 | TEST(FunctionDispatcherBuiltins, FindFirstWithKeyValue) { 39 | Json::Reader reader; 40 | Json::Value value; 41 | CHECK(reader.parse(R"([ { "K1" : "V1" } , 42 | { "K2" : "V2", "foo" : "bar" }, 43 | { "K2" : "V2"}, 44 | { "K3" : { "lower" : "l" }} 45 | ])", 46 | value)); 47 | 48 | EXPECT_EQ(-1, FindFirstWithKeyValue(value, "K", "V1")); 49 | EXPECT_EQ(-1, FindFirstWithKeyValue(value, "K1", "")); 50 | EXPECT_EQ(0, FindFirstWithKeyValue(value, "K1", "V1")); 51 | EXPECT_EQ(1, FindFirstWithKeyValue(value, "K2", "V2")); 52 | 53 | Json::Value lower_value; 54 | CHECK(reader.parse(R"({ "lower" : "l" })", lower_value)); 55 | EXPECT_EQ(3, FindFirstWithKeyValue(value, "K3", lower_value)); 56 | 57 | // Specifying a path as key does not work. 58 | EXPECT_EQ(-1, FindFirstWithKeyValue(value, "K3.lower", "l")); 59 | } 60 | 61 | } // namespace 62 | } // namespace builtin 63 | } // namespace state_chart 64 | -------------------------------------------------------------------------------- /statechart/internal/function_dispatcher_impl.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/function_dispatcher_impl.h" 16 | 17 | #include 18 | 19 | #include "statechart/internal/function_dispatcher_builtin.h" 20 | #include "statechart/platform/map_util.h" 21 | 22 | namespace state_chart { 23 | 24 | namespace internal { 25 | 26 | std::vector JsonValuesToStrings( 27 | const std::vector& values) { 28 | std::vector json_strs; 29 | for (const auto* json_value : values) { 30 | json_strs.push_back(Json::FastWriter().write(*json_value)); 31 | } 32 | return json_strs; 33 | } 34 | 35 | } // namespace internal 36 | 37 | FunctionDispatcherImpl::FunctionDispatcherImpl() { 38 | RegisterFunction("ContainsKey", &builtin::ContainsKey); 39 | RegisterFunction("FindFirstWithKeyValue", &builtin::FindFirstWithKeyValue); 40 | } 41 | 42 | FunctionDispatcherImpl::FunctionDispatcherImpl( 43 | const FunctionDispatcherImpl& other) { 44 | for (const auto& entry : other.function_map_) { 45 | function_map_.emplace(entry.first, entry.second->Clone()); 46 | } 47 | } 48 | 49 | // override 50 | bool FunctionDispatcherImpl::HasFunction(const string& function_name) const { 51 | return gtl::ContainsKey(function_map_, function_name); 52 | } 53 | 54 | // override 55 | bool FunctionDispatcherImpl::Execute( 56 | const string& function_name, const std::vector& inputs, 57 | Json::Value* return_value) { 58 | if (!HasFunction(function_name)) { 59 | LOG(INFO) << "No function registered for name : " << function_name; 60 | return false; 61 | } 62 | return function_map_.at(function_name)->Execute(inputs, return_value); 63 | } 64 | 65 | } // namespace state_chart 66 | -------------------------------------------------------------------------------- /statechart/internal/model.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model.h" 16 | 17 | #include "absl/strings/match.h" 18 | 19 | namespace state_chart { 20 | 21 | // static 22 | bool Model::EventMatches(const string& event_name, 23 | const std::vector& events) { 24 | // Search the list of event names. 25 | for (const string& transition_event : events) { 26 | // Wildcard matches all. 27 | if (transition_event == "*") { 28 | return true; 29 | } 30 | // The transition_event must be a prefix of event in the event hierarchy. 31 | // For example, the transition events, 'event_A' and 'event_A.sub_event_B', 32 | // will match the event, 'event_A.sub_event_B.something_else'. 33 | if (absl::StartsWith(event_name, transition_event) && 34 | (event_name.size() == transition_event.size() || 35 | event_name[transition_event.size()] == '.')) { 36 | return true; 37 | } 38 | } // for event_to_match 39 | return false; 40 | } 41 | 42 | } // namespace state_chart 43 | -------------------------------------------------------------------------------- /statechart/internal/model/assign.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/assign.h" 16 | 17 | #include 18 | 19 | #include "absl/strings/str_cat.h" 20 | #include "absl/strings/substitute.h" 21 | #include "statechart/internal/datamodel.h" 22 | #include "statechart/internal/runtime.h" 23 | #include "statechart/internal/utility.h" 24 | 25 | namespace state_chart { 26 | namespace model { 27 | 28 | Assign::Assign(const string& location, const string& expr) 29 | : location_(location), expr_(expr) {} 30 | 31 | // override 32 | bool Assign::Execute(Runtime* runtime) const { 33 | VLOG(1) << absl::Substitute("Assign($0, $1)", location_, expr_); 34 | if (!runtime->mutable_datamodel()->AssignExpression(location_, expr_)) { 35 | runtime->EnqueueExecutionError( 36 | absl::StrCat("'Assign' failure for: ", location_, " = ", expr_)); 37 | return false; 38 | } 39 | return true; 40 | } 41 | 42 | } // namespace model 43 | } // namespace state_chart 44 | -------------------------------------------------------------------------------- /statechart/internal/model/assign.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_ASSIGN_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_ASSIGN_H_ 22 | 23 | #include 24 | 25 | #include "statechart/platform/types.h" 26 | #include "statechart/internal/model/executable_content.h" 27 | 28 | namespace state_chart { 29 | class Runtime; 30 | } 31 | 32 | namespace state_chart { 33 | namespace model { 34 | 35 | class Assign : public ExecutableContent { 36 | public: 37 | Assign(const string& location, const string& expr); 38 | Assign(const Assign&) = delete; 39 | Assign& operator=(const Assign&) = delete; 40 | 41 | bool Execute(Runtime* runtime) const override; 42 | 43 | private: 44 | const string location_; 45 | const string expr_; 46 | }; 47 | 48 | } // namespace model 49 | } // namespace state_chart 50 | 51 | #endif // STATE_CHART_INTERNAL_MODEL_ASSIGN_H_ 52 | -------------------------------------------------------------------------------- /statechart/internal/model/assign_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/assign.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "statechart/internal/testing/mock_datamodel.h" 21 | #include "statechart/internal/testing/mock_runtime.h" 22 | 23 | namespace state_chart { 24 | namespace model { 25 | namespace { 26 | 27 | using ::testing::_; 28 | using ::testing::Return; 29 | 30 | TEST(AssignTest, EvaluationError) { 31 | Assign assign("location", "expression"); 32 | 33 | MockRuntime runtime; 34 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), 35 | AssignExpression("location", "expression")) 36 | .WillOnce(Return(false)); 37 | EXPECT_CALL(runtime, EnqueueInternalEvent("error.execution", _)); 38 | EXPECT_FALSE(assign.Execute(&runtime)); 39 | } 40 | 41 | TEST(AssignTest, ValidLocation) { 42 | Assign assign("location", "expression"); 43 | 44 | MockRuntime runtime; 45 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), 46 | AssignExpression("location", "expression")) 47 | .WillOnce(Return(true)); 48 | EXPECT_TRUE(assign.Execute(&runtime)); 49 | } 50 | 51 | } // namespace 52 | } // namespace model 53 | } // namespace state_chart 54 | -------------------------------------------------------------------------------- /statechart/internal/model/data.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/data.h" 16 | 17 | #include 18 | 19 | #include "absl/strings/str_cat.h" 20 | #include "absl/strings/substitute.h" 21 | #include "statechart/internal/datamodel.h" 22 | #include "statechart/internal/runtime.h" 23 | #include "statechart/internal/utility.h" 24 | 25 | namespace state_chart { 26 | namespace model { 27 | 28 | Data::Data(const string& location, const string& expr) 29 | : location_(location), expr_(expr) {} 30 | 31 | // override 32 | bool Data::Execute(Runtime* runtime) const { 33 | VLOG(1) << absl::Substitute("Data($0, $1)", location_, expr_); 34 | if (!runtime->mutable_datamodel()->Declare(location_)) { 35 | runtime->EnqueueExecutionError( 36 | absl::StrCat("'Data' declaration failure at location: ", location_)); 37 | return false; 38 | } 39 | if (!expr_.empty() && 40 | !runtime->mutable_datamodel()->AssignExpression(location_, expr_)) { 41 | runtime->EnqueueExecutionError( 42 | absl::StrCat("'Data' assign failure: ", location_, " = ", 43 | expr_.empty() ? "[empty]" : expr_)); 44 | return false; 45 | } 46 | return true; 47 | } 48 | 49 | } // namespace model 50 | } // namespace state_chart 51 | -------------------------------------------------------------------------------- /statechart/internal/model/data.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_DATA_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_DATA_H_ 22 | 23 | #include 24 | 25 | #include "statechart/platform/types.h" 26 | #include "statechart/internal/model/executable_content.h" 27 | 28 | namespace state_chart { 29 | class Runtime; 30 | } 31 | 32 | namespace state_chart { 33 | namespace model { 34 | 35 | // The data element as an executable content. When executed, declare the 36 | // variable that this represents. Currently 'src' method for defining the value 37 | // is not supported. 38 | class Data : public ExecutableContent { 39 | public: 40 | Data(const string& location, const string& expr); 41 | Data(const Data&) = delete; 42 | Data& operator=(const Data&) = delete; 43 | 44 | bool Execute(Runtime* runtime) const override; 45 | 46 | private: 47 | string location_; 48 | string expr_; 49 | }; 50 | 51 | } // namespace model 52 | } // namespace state_chart 53 | 54 | #endif // STATE_CHART_INTERNAL_MODEL_DATA_H_ 55 | -------------------------------------------------------------------------------- /statechart/internal/model/data_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/data.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "statechart/internal/testing/mock_datamodel.h" 21 | #include "statechart/internal/testing/mock_runtime.h" 22 | 23 | namespace state_chart { 24 | namespace model { 25 | namespace { 26 | 27 | using ::testing::_; 28 | using ::testing::Return; 29 | 30 | TEST(DataTest, EvaluationError) { 31 | Data data("location", "expression"); 32 | 33 | MockRuntime runtime; 34 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), Declare("location")) 35 | .WillOnce(Return(false)); 36 | EXPECT_CALL(runtime, EnqueueInternalEvent("error.execution", _)); 37 | EXPECT_FALSE(data.Execute(&runtime)); 38 | 39 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), Declare("location")) 40 | .WillOnce(Return(true)); 41 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), 42 | AssignExpression("location", "expression")) 43 | .WillOnce(Return(false)); 44 | EXPECT_CALL(runtime, EnqueueInternalEvent("error.execution", _)); 45 | EXPECT_FALSE(data.Execute(&runtime)); 46 | } 47 | 48 | TEST(DataTest, ValidDeclaration) { 49 | Data data("location", "expression"); 50 | 51 | MockRuntime runtime; 52 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), Declare("location")) 53 | .WillOnce(Return(true)); 54 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), 55 | AssignExpression("location", "expression")) 56 | .WillOnce(Return(true)); 57 | EXPECT_TRUE(data.Execute(&runtime)); 58 | } 59 | 60 | } // namespace 61 | } // namespace model 62 | } // namespace state_chart 63 | -------------------------------------------------------------------------------- /statechart/internal/model/executable_block.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_EXECUTABLE_BLOCK_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_EXECUTABLE_BLOCK_H_ 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "statechart/internal/model/executable_content.h" 29 | 30 | namespace state_chart { 31 | class Runtime; 32 | } 33 | 34 | namespace state_chart { 35 | namespace model { 36 | 37 | // A class that encapsulates a sequence of executable content. 38 | class ExecutableBlock : public ExecutableContent { 39 | public: 40 | // Skips over items in 'executables' which are nullptr. 41 | explicit ExecutableBlock( 42 | const std::vector& executables) { 43 | for (auto executable : executables) { 44 | if (executable != nullptr) { 45 | executables_.push_back(executable); 46 | } else { 47 | LOG(DFATAL) << "Not adding null executable block to executable content" 48 | << std::endl; 49 | } 50 | } 51 | } 52 | 53 | ExecutableBlock(const ExecutableBlock&) = delete; 54 | ExecutableBlock& operator=(const ExecutableBlock&) = delete; 55 | 56 | ~ExecutableBlock() override = default; 57 | 58 | // Executes all executables in sequence order. Stop execution of the block if 59 | // any executable raises an error (returns false). 60 | bool Execute(Runtime* runtime) const override { 61 | for (auto executable : executables_) { 62 | if (!executable->Execute(runtime)) { 63 | return false; 64 | } 65 | } 66 | return true; 67 | } 68 | 69 | private: 70 | std::vector executables_; 71 | }; 72 | 73 | } // namespace model 74 | } // namespace state_chart 75 | 76 | #endif // STATE_CHART_INTERNAL_MODEL_EXECUTABLE_BLOCK_H_ 77 | -------------------------------------------------------------------------------- /statechart/internal/model/executable_block_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/executable_block.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "statechart/internal/testing/mock_executable_content.h" 22 | #include "statechart/internal/testing/mock_runtime.h" 23 | 24 | namespace state_chart { 25 | namespace model { 26 | namespace { 27 | 28 | using ::testing::Return; 29 | 30 | TEST(ExecutableBlock, ExecutableBlockOrderingTest) { 31 | MockExecutableContent exec1, exec2, exec3, exec4; 32 | MockRuntime runtime; 33 | 34 | testing::InSequence sequence; 35 | EXPECT_CALL(exec1, Execute(&runtime)).WillOnce(Return(true)); 36 | EXPECT_CALL(exec2, Execute(&runtime)).WillOnce(Return(true)); 37 | EXPECT_CALL(exec3, Execute(&runtime)).WillOnce(Return(true)); 38 | EXPECT_CALL(exec4, Execute(&runtime)).WillOnce(Return(true)); 39 | 40 | ExecutableBlock block({&exec1, &exec2, &exec3, &exec4}); 41 | EXPECT_TRUE(block.Execute(&runtime)); 42 | } 43 | 44 | TEST(ExecutableBlock, ExecutableBlockOrderingTestWithError) { 45 | MockExecutableContent exec1, exec2, exec3, exec4; 46 | MockRuntime runtime; 47 | 48 | testing::InSequence sequence; 49 | EXPECT_CALL(exec1, Execute(&runtime)).WillOnce(Return(true)); 50 | EXPECT_CALL(exec2, Execute(&runtime)).WillOnce(Return(true)); 51 | EXPECT_CALL(exec3, Execute(&runtime)).WillOnce(Return(false)); 52 | EXPECT_CALL(exec4, Execute(&runtime)).Times(0); 53 | 54 | ExecutableBlock block({&exec1, &exec2, &exec3, &exec4}); 55 | EXPECT_FALSE(block.Execute(&runtime)); 56 | } 57 | 58 | } // namespace 59 | } // namespace model 60 | } // namespace state_chart 61 | -------------------------------------------------------------------------------- /statechart/internal/model/executable_content.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_EXECUTABLE_CONTENT_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_EXECUTABLE_CONTENT_H_ 22 | 23 | #include "statechart/internal/model/model_element.h" 24 | 25 | namespace state_chart { 26 | class Runtime; 27 | } 28 | 29 | namespace state_chart { 30 | namespace model { 31 | 32 | // This class executes operation(s) on a Runtime. 33 | class ExecutableContent : public ModelElement { 34 | public: 35 | // Returns true if there were no errors. 36 | virtual bool Execute(Runtime* runtime) const = 0; 37 | }; 38 | 39 | } // namespace model 40 | } // namespace state_chart 41 | 42 | #endif // STATE_CHART_INTERNAL_MODEL_EXECUTABLE_CONTENT_H_ 43 | -------------------------------------------------------------------------------- /statechart/internal/model/for_each.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/for_each.h" 16 | 17 | #include 18 | 19 | #include "absl/strings/str_cat.h" 20 | #include "absl/strings/substitute.h" 21 | #include "statechart/internal/datamodel.h" 22 | #include "statechart/internal/model/executable_content.h" 23 | #include "statechart/internal/runtime.h" 24 | #include "statechart/internal/utility.h" 25 | 26 | namespace state_chart { 27 | namespace model { 28 | 29 | ForEach::ForEach(const string& array, const string& item, const string& index, 30 | const ExecutableContent* body) 31 | : array_(array), item_(item), index_(index), body_(body) {} 32 | 33 | // override 34 | bool ForEach::Execute(Runtime* runtime) const { 35 | VLOG(1) << absl::Substitute("ForEach(<$0, $1> : $2)", index_, item_, array_); 36 | auto* datamodel = runtime->mutable_datamodel(); 37 | // Get the iterator. 38 | auto iterator = datamodel->EvaluateIterator(array_); 39 | if (iterator == nullptr) { 40 | runtime->EnqueueExecutionError(absl::StrCat( 41 | "'ForEach' unable to get iterator for collection: ", array_)); 42 | return false; 43 | } 44 | // Create item variable if needed. 45 | if (!datamodel->IsDefined(item_) && !datamodel->Declare(item_)) { 46 | runtime->EnqueueExecutionError( 47 | absl::StrCat("'ForEach' unable to declare item variable at: ", item_)); 48 | return false; 49 | } 50 | // Create the index variable if specified. 51 | if (!index_.empty() && !datamodel->IsDefined(index_) && 52 | !datamodel->Declare(index_)) { 53 | runtime->EnqueueExecutionError(absl::StrCat( 54 | "'ForEach' unable to declare index variable at: ", index_)); 55 | return false; 56 | } 57 | 58 | // Execute the loop. 59 | string value; 60 | string index; 61 | for (; !iterator->AtEnd(); iterator->Next()) { 62 | value = iterator->GetValue(); 63 | // Assign the item. 64 | if (!datamodel->AssignExpression(item_, value)) { 65 | runtime->EnqueueExecutionError( 66 | absl::StrCat("'ForEach' unable to assign item variable '", item_, 67 | "' with value: ", value)); 68 | return false; 69 | } 70 | // Assign the index if needed. 71 | if (!index_.empty()) { 72 | index = iterator->GetIndex(); 73 | if (!datamodel->AssignExpression(index_, index)) { 74 | runtime->EnqueueExecutionError( 75 | absl::StrCat("'ForEach' unable to assign index variable '", index_, 76 | "' with value: ", index)); 77 | return false; 78 | } 79 | } 80 | // Run the loop body, empty executable blocks are nullptr. 81 | if (body_ != nullptr && !body_->Execute(runtime)) { 82 | return false; 83 | } 84 | } 85 | return true; 86 | } 87 | 88 | } // namespace model 89 | } // namespace state_chart 90 | -------------------------------------------------------------------------------- /statechart/internal/model/for_each.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_FOR_EACH_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_FOR_EACH_H_ 22 | 23 | #include "statechart/platform/types.h" 24 | #include "statechart/internal/model/executable_content.h" 25 | 26 | namespace state_chart { 27 | class Runtime; 28 | } 29 | 30 | namespace state_chart { 31 | namespace model { 32 | 33 | // Encapsulates execution logic for a 'foreach' element in state charts. 34 | class ForEach : public ExecutableContent { 35 | public: 36 | // Params: 37 | // array Location expression of the iterable collection (array). 38 | // item Location expression of the item (value). 39 | // index Location expression of the index, may be empty to ignore. 40 | // body The body of the foreach, usually an ExecutableBlock. Does not take 41 | // ownership. 42 | ForEach(const string& array, const string& item, const string& index, 43 | const ExecutableContent* body); 44 | 45 | ForEach(const ForEach&) = delete; 46 | ForEach& operator=(const ForEach&) = delete; 47 | 48 | 49 | ~ForEach() override = default; 50 | 51 | // If an error occurs while running the body of the foreach, the loop is 52 | // terminated and false returned. 53 | bool Execute(Runtime* runtime) const override; 54 | 55 | private: 56 | const string array_; 57 | const string item_; 58 | const string index_; 59 | const ExecutableContent* const body_; 60 | }; 61 | 62 | } // namespace model 63 | } // namespace state_chart 64 | 65 | #endif // STATE_CHART_INTERNAL_MODEL_FOR_EACH_H_ 66 | -------------------------------------------------------------------------------- /statechart/internal/model/if.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/if.h" 16 | 17 | #include "absl/strings/str_cat.h" 18 | #include "absl/strings/str_join.h" 19 | #include "statechart/internal/datamodel.h" 20 | #include "statechart/internal/runtime.h" 21 | #include "statechart/internal/utility.h" 22 | #include "statechart/logging.h" 23 | 24 | namespace state_chart { 25 | namespace model { 26 | 27 | namespace { 28 | 29 | struct KeyFormatter { 30 | template 31 | void operator()(std::string* out, Pair pair) const { 32 | out->append(pair.first); 33 | } 34 | }; 35 | 36 | } // namespace 37 | 38 | 39 | If::If(const std::vector>& 40 | condition_executable) 41 | : condition_executable_(condition_executable) {} 42 | 43 | // override 44 | bool If::Execute(Runtime* runtime) const { 45 | VLOG(1) << absl::StrCat( 46 | "If conditions: ", 47 | absl::StrJoin(condition_executable_, ", ", KeyFormatter())); 48 | 49 | bool saw_empty = false; 50 | bool no_error = true; 51 | for (const auto& cond_executable : condition_executable_) { 52 | RETURN_FALSE_IF_MSG(saw_empty, 53 | "Empty conditions in executable must come last."); 54 | 55 | const string& cond = cond_executable.first; 56 | auto* executable = cond_executable.second; 57 | 58 | bool result = cond.empty(); // Empty cond evaluates to true. 59 | if (!result && 60 | !runtime->datamodel().EvaluateBooleanExpression(cond, &result)) { 61 | runtime->EnqueueExecutionError( 62 | absl::StrCat("'If' condition failed to evaluate: ", cond)); 63 | no_error = false; 64 | continue; 65 | } 66 | 67 | if (result) { 68 | // Empty executable blocks are nullptr. 69 | if (executable != nullptr) { 70 | executable->Execute(runtime); 71 | } 72 | return no_error; 73 | } 74 | 75 | saw_empty |= cond.empty(); 76 | } 77 | return no_error; 78 | } 79 | 80 | } // namespace model 81 | } // namespace state_chart 82 | -------------------------------------------------------------------------------- /statechart/internal/model/if.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_IF_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_IF_H_ 22 | 23 | #include 24 | #include 25 | 26 | #include "statechart/platform/types.h" 27 | #include "statechart/internal/model/executable_content.h" 28 | 29 | namespace state_chart { 30 | class Runtime; 31 | } // namespace state_chart 32 | 33 | namespace state_chart { 34 | namespace model { 35 | 36 | class If : public ExecutableContent { 37 | public: 38 | // Accepts a list of pairs of condition, executable. The logic for If will 39 | // loop through the configuration, evaluate the condition and execute the 40 | // first executable for which the condition evaluates to true. 41 | explicit If(const std::vector>& 42 | condition_executable); 43 | If(const If&) = delete; 44 | If& operator=(const If&) = delete; 45 | 46 | bool Execute(Runtime* runtime) const override; 47 | 48 | private: 49 | const std::vector> 50 | condition_executable_; 51 | }; 52 | 53 | } // namespace model 54 | } // namespace state_chart 55 | 56 | #endif // STATE_CHART_INTERNAL_MODEL_IF_H_ 57 | -------------------------------------------------------------------------------- /statechart/internal/model/log.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/log.h" 16 | 17 | #include 18 | 19 | #include "absl/strings/str_cat.h" 20 | #include "statechart/internal/datamodel.h" 21 | #include "statechart/internal/runtime.h" 22 | #include "statechart/internal/utility.h" 23 | 24 | namespace state_chart { 25 | namespace model { 26 | 27 | Log::Log(const string& label, const string& expr) 28 | : label_(label), expr_(expr) {} 29 | 30 | // override 31 | bool Log::Execute(Runtime* runtime) const { 32 | VLOG(1) << "Log: " << label_ << ": " << expr_; 33 | string log_string; 34 | if (!runtime->datamodel().EvaluateStringExpression(expr_, &log_string)) { 35 | runtime->EnqueueExecutionError( 36 | absl::StrCat("'Log' expression failed to evaluate to string: ", expr_)); 37 | return false; 38 | } 39 | LOG(INFO) << (label_.empty() ? "" : label_ + ": ") << log_string; 40 | return true; 41 | } 42 | 43 | } // namespace model 44 | } // namespace state_chart 45 | -------------------------------------------------------------------------------- /statechart/internal/model/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_LOG_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_LOG_H_ 22 | 23 | #include 24 | 25 | #include "statechart/platform/types.h" 26 | #include "statechart/internal/model/executable_content.h" 27 | 28 | namespace state_chart { 29 | class Runtime; 30 | } 31 | 32 | namespace state_chart { 33 | namespace model { 34 | 35 | class Log : public ExecutableContent { 36 | public: 37 | Log(const string& label, const string& expr); 38 | Log(const Log&) = delete; 39 | Log& operator=(const Log&) = delete; 40 | 41 | bool Execute(Runtime* runtime) const override; 42 | 43 | private: 44 | const string label_; 45 | const string expr_; 46 | }; 47 | 48 | } // namespace model 49 | } // namespace state_chart 50 | 51 | #endif // STATE_CHART_INTERNAL_MODEL_LOG_H_ 52 | -------------------------------------------------------------------------------- /statechart/internal/model/log_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/log.h" 16 | 17 | #include "statechart/platform/test_util.h" 18 | #include "statechart/internal/testing/mock_datamodel.h" 19 | #include "statechart/internal/testing/mock_runtime.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | namespace state_chart { 26 | namespace model { 27 | namespace { 28 | 29 | using ::testing::_; 30 | using ::testing::kDoNotCaptureLogsYet; 31 | using ::testing::NotNull; 32 | using ::testing::ScopedMockLog; 33 | 34 | const char kLogCCPath[] = "statechart/internal/model/log.cc"; 35 | 36 | TEST(LogTest, Error) { 37 | MockRuntime runtime; 38 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), 39 | EvaluateStringExpression("expression", NotNull())) 40 | .WillOnce(ReturnEvaluationError()); 41 | EXPECT_CALL(runtime, EnqueueInternalEvent("error.execution", _)); 42 | 43 | ScopedMockLog mock_log(kDoNotCaptureLogsYet); 44 | EXPECT_CALL(mock_log, Log(base_logging::INFO, kLogCCPath, _)).Times(0); 45 | 46 | Log log("", "expression"); 47 | mock_log.StartCapturingLogs(); 48 | EXPECT_FALSE(log.Execute(&runtime)); 49 | } 50 | 51 | TEST(LogTest, NoLabel) { 52 | MockRuntime runtime; 53 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), 54 | EvaluateStringExpression("expression", NotNull())) 55 | .WillOnce(ReturnEvaluationResult("log line")); 56 | 57 | ScopedMockLog mock_log(kDoNotCaptureLogsYet); 58 | EXPECT_CALL(mock_log, Log(base_logging::INFO, kLogCCPath, "log line")); 59 | 60 | Log log("", "expression"); 61 | mock_log.StartCapturingLogs(); 62 | EXPECT_TRUE(log.Execute(&runtime)); 63 | } 64 | 65 | TEST(LogTest, Label) { 66 | MockRuntime runtime; 67 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), 68 | EvaluateStringExpression("expression", NotNull())) 69 | .WillOnce(ReturnEvaluationResult("log line")); 70 | 71 | ScopedMockLog mock_log(kDoNotCaptureLogsYet); 72 | EXPECT_CALL(mock_log, Log(base_logging::INFO, kLogCCPath, "label: log line")); 73 | 74 | Log log("label", "expression"); 75 | mock_log.StartCapturingLogs(); 76 | log.Execute(&runtime); 77 | } 78 | 79 | } // namespace 80 | } // namespace model 81 | } // namespace state_chart 82 | -------------------------------------------------------------------------------- /statechart/internal/model/model.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_MODEL_MODEL_H_ 18 | #define STATE_CHART_INTERNAL_MODEL_MODEL_H_ 19 | 20 | #include "statechart/internal/model/assign.h" // IWYU pragma: export 21 | #include "statechart/internal/model/data.h" // IWYU pragma: export 22 | #include "statechart/internal/model/executable_block.h" // IWYU pragma: export 23 | #include "statechart/internal/model/executable_content.h" // IWYU pragma: export 24 | #include "statechart/internal/model/for_each.h" // IWYU pragma: export 25 | #include "statechart/internal/model/if.h" // IWYU pragma: export 26 | #include "statechart/internal/model/log.h" // IWYU pragma: export 27 | #include "statechart/internal/model/model_element.h" // IWYU pragma: export 28 | #include "statechart/internal/model/raise.h" // IWYU pragma: export 29 | #include "statechart/internal/model/send.h" // IWYU pragma: export 30 | #include "statechart/internal/model/state.h" // IWYU pragma: export 31 | #include "statechart/internal/model/transition.h" // IWYU pragma: export 32 | 33 | #endif // STATE_CHART_INTERNAL_MODEL_MODEL_H_ 34 | -------------------------------------------------------------------------------- /statechart/internal/model/model_element.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/model_element.h" 16 | 17 | namespace state_chart { 18 | namespace model { 19 | ModelElement::~ModelElement() {} 20 | } // namespace model 21 | } // namespace state_chart 22 | -------------------------------------------------------------------------------- /statechart/internal/model/model_element.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_MODEL_ELEMENT_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_MODEL_ELEMENT_H_ 22 | 23 | namespace state_chart { 24 | namespace model { 25 | 26 | // A root superclass used for memory management of all instances of model 27 | // related classes except the model itself. 28 | // It only contains a single virtual destructor. 29 | class ModelElement { 30 | public: 31 | virtual ~ModelElement(); 32 | }; 33 | 34 | } // namespace model 35 | } // namespace state_chart 36 | 37 | #endif // STATE_CHART_INTERNAL_MODEL_MODEL_ELEMENT_H_ 38 | -------------------------------------------------------------------------------- /statechart/internal/model/raise.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/raise.h" 16 | 17 | #include 18 | 19 | #include "statechart/internal/runtime.h" 20 | 21 | namespace state_chart { 22 | namespace model { 23 | 24 | Raise::Raise(const string& event) : event_(event) {} 25 | 26 | // override 27 | bool Raise::Execute(Runtime* runtime) const { 28 | VLOG(1) << "Raise: " << event_; 29 | runtime->EnqueueInternalEvent(event_, ""); 30 | return true; 31 | } 32 | 33 | } // namespace model 34 | } // namespace state_chart 35 | -------------------------------------------------------------------------------- /statechart/internal/model/raise.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_RAISE_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_RAISE_H_ 22 | 23 | #include 24 | 25 | #include "statechart/platform/types.h" 26 | #include "statechart/internal/model/executable_content.h" 27 | 28 | namespace state_chart { 29 | class Runtime; 30 | } 31 | 32 | namespace state_chart { 33 | namespace model { 34 | 35 | class Raise : public ExecutableContent { 36 | public: 37 | explicit Raise(const string& event); 38 | Raise(const Raise&) = delete; 39 | Raise& operator=(const Raise&) = delete; 40 | 41 | // Enqueues an internal event by calling runtime->EnqueueInternalEvent(). 42 | bool Execute(Runtime* runtime) const override; 43 | 44 | private: 45 | const string event_; 46 | }; 47 | 48 | } // namespace model 49 | } // namespace state_chart 50 | 51 | #endif // STATE_CHART_INTERNAL_MODEL_RAISE_H_ 52 | -------------------------------------------------------------------------------- /statechart/internal/model/raise_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/raise.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "statechart/internal/testing/mock_runtime.h" 21 | 22 | namespace state_chart { 23 | namespace model { 24 | namespace { 25 | 26 | TEST(RaiseTest, All) { 27 | Raise raise("event"); 28 | 29 | MockRuntime runtime; 30 | EXPECT_CALL(runtime, EnqueueInternalEvent("event", "")); 31 | raise.Execute(&runtime); 32 | } 33 | 34 | } // namespace 35 | } // namespace model 36 | } // namespace state_chart 37 | -------------------------------------------------------------------------------- /statechart/internal/model/send.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/send.h" 16 | 17 | #include 18 | 19 | #include "absl/strings/str_cat.h" 20 | #include "statechart/internal/datamodel.h" 21 | #include "statechart/internal/event_dispatcher.h" 22 | #include "statechart/internal/runtime.h" 23 | #include "statechart/internal/utility.h" 24 | #include "statechart/logging.h" 25 | #include "statechart/platform/map_util.h" 26 | 27 | namespace state_chart { 28 | namespace model { 29 | 30 | Send::Send(const StrOrExpr& event, const StrOrExpr& target, const StrOrExpr& id, 31 | const StrOrExpr& type) 32 | : event_(event), target_(target), id_(id), type_(type) {} 33 | 34 | // override 35 | bool Send::Execute(Runtime* runtime) const { 36 | VLOG(1) << "Send: event = " << event_.Value() 37 | << ", target = " << target_.Value(); 38 | Datamodel* datamodel = runtime->mutable_datamodel(); 39 | 40 | // Evaluate string expressions for send attributes. 41 | static const char* const kAttributeName[] = {"event", "target", "type", "id"}; 42 | const StrOrExpr* string_attr[] = {&event_, &target_, &type_, &id_}; 43 | 44 | static_assert(ABSL_ARRAYSIZE(kAttributeName) == ABSL_ARRAYSIZE(string_attr), 45 | "Array sizes must be equal."); 46 | 47 | std::vector string_attr_value(ABSL_ARRAYSIZE(string_attr), ""); 48 | 49 | for (unsigned int i = 0; i < ABSL_ARRAYSIZE(string_attr); ++i) { 50 | if (!string_attr[i]->IsEmpty() && 51 | !string_attr[i]->Evaluate(datamodel, &string_attr_value[i])) { 52 | runtime->EnqueueExecutionError(absl::StrCat( 53 | "'Send' attribute '", kAttributeName[i], 54 | "' failed to evaluate value: ", string_attr[i]->Value())); 55 | return false; 56 | } 57 | } 58 | 59 | // Evaluate expressions for send parameters. 60 | std::map evaluated_data; 61 | string result; 62 | bool no_error = true; 63 | for (const auto& data : parameters_) { 64 | result.clear(); 65 | // If there is an error, the specifications say to ignore the parameter. 66 | // Hence we continue to process the rest. 67 | if (!datamodel->EvaluateExpression(data.second, &result)) { 68 | runtime->EnqueueExecutionError( 69 | absl::StrCat("'Send' parameter '", data.first, 70 | "' failed to evaluate value: ", data.second)); 71 | no_error = false; 72 | } else { 73 | evaluated_data[data.first] = result; 74 | } 75 | } 76 | 77 | runtime->GetEventDispatcher()->NotifySendEvent( 78 | runtime, string_attr_value[0], string_attr_value[1], string_attr_value[2], 79 | string_attr_value[3], datamodel->EncodeParameters(evaluated_data)); 80 | return no_error; 81 | } 82 | 83 | bool Send::AddParamByExpression(const string& key, const string& expr) { 84 | RETURN_FALSE_IF(expr.empty()); 85 | gtl::InsertIfNotPresent(¶meters_, key, expr); 86 | return true; 87 | } 88 | 89 | } // namespace model 90 | } // namespace state_chart 91 | -------------------------------------------------------------------------------- /statechart/internal/model/send.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_SEND_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_SEND_H_ 22 | 23 | #include 24 | #include 25 | 26 | #include "statechart/platform/types.h" 27 | #include "statechart/internal/model/executable_content.h" 28 | #include "statechart/internal/model/str_or_expr.h" 29 | 30 | namespace state_chart { 31 | class Runtime; 32 | } 33 | 34 | namespace state_chart { 35 | namespace model { 36 | 37 | // This class implements the Send action. It contains a key-value map of data 38 | // parameters to be passed to the external service. All expressions are 39 | // evaluated against the current datamodel (in runtime) when Execute() is 40 | // called. Then listeners of Send are notified immediately. 41 | // Note that Execute() will not notify listeners if the evaluation of any 42 | // attribute fails. However, when the evaluation of a parameter fails, 43 | // notification proceeds with the failing parameter ignored. This behaviour 44 | // is defined in the state chart specification for . 45 | class Send : public ExecutableContent { 46 | public: 47 | // Create send with its attributes that can either be a static string or an 48 | // expression to be evaluated at runtime. 49 | Send(const StrOrExpr& event, const StrOrExpr& target, const StrOrExpr& id, 50 | const StrOrExpr& type); 51 | Send(const Send&) = delete; 52 | Send& operator=(const Send&) = delete; 53 | 54 | ~Send() override = default; 55 | 56 | bool Execute(Runtime* runtime) const override; 57 | 58 | // Add a parameter by giving it a key (name) and a expression to evaluate 59 | // in the datamodel. 60 | // Does nothing if key was already added. Returns false if 'expr' is empty. 61 | // Note that this behaviour differs from the state chart specification as the 62 | // specification allows duplicate parameters. 63 | // Returns false if an error occurred. 64 | bool AddParamByExpression(const string& key, const string& expr); 65 | 66 | // Add a parameter based on a datamodel location name. The key of the 67 | // parameter is the name of the datamodel location and the expression is the 68 | // datamodel location (that evaluates to its value when this is executed). 69 | // Does nothing if key was already added. 70 | void AddParamById(const string& location) { 71 | AddParamByExpression(location, location); 72 | } 73 | 74 | private: 75 | // Send attribute expressions. 76 | StrOrExpr event_; 77 | StrOrExpr target_; 78 | StrOrExpr id_; 79 | StrOrExpr type_; 80 | 81 | // A map of parameter name to value expressions to be evaluated before being 82 | // passed to an external service when send is executed. 83 | std::map parameters_; 84 | }; 85 | 86 | } // namespace model 87 | } // namespace state_chart 88 | 89 | #endif // STATE_CHART_INTERNAL_MODEL_SEND_H_ 90 | -------------------------------------------------------------------------------- /statechart/internal/model/state.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/state.h" 16 | 17 | #include "statechart/internal/model/transition.h" 18 | #include "statechart/logging.h" 19 | 20 | namespace state_chart { 21 | namespace model { 22 | 23 | State::State(const string& id, bool is_final, bool is_parallel, 24 | const ExecutableContent* datamodel, 25 | const ExecutableContent* on_entry, 26 | const ExecutableContent* on_exit) 27 | : id_(id), 28 | is_final_(is_final), 29 | is_parallel_(is_parallel), 30 | parent_(nullptr), 31 | initial_transition_(nullptr), 32 | datamodel_(datamodel), 33 | on_entry_(on_entry), 34 | on_exit_(on_exit) {} 35 | 36 | bool State::SetInitialTransition(const Transition* initial_transition) { 37 | RETURN_FALSE_IF_MSG( 38 | IsFinal(), "Final (atomic) states may not have an initial transition."); 39 | RETURN_FALSE_IF(initial_transition->GetSourceState() != this); 40 | initial_transition_ = initial_transition; 41 | return true; 42 | } 43 | 44 | void State::AddChildren(const std::vector& child_states) { 45 | for (auto* child : child_states) { 46 | AddChild(child); 47 | } 48 | } 49 | 50 | } // namespace model 51 | } // namespace state_chart 52 | -------------------------------------------------------------------------------- /statechart/internal/model/state.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_STATE_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_STATE_H_ 22 | 23 | #include 24 | #include 25 | 26 | #include "statechart/platform/types.h" 27 | #include "statechart/internal/model/model_element.h" 28 | 29 | namespace state_chart { 30 | namespace model { 31 | 32 | class ExecutableContent; 33 | class Transition; 34 | 35 | // Basic implementation of the State. 36 | class State : public ModelElement { 37 | public: 38 | // Params: 39 | // id: Unique state id. 40 | // is_final: Marks this state as a final state. 41 | // is_parallel: Marks this state as a parallel state. 42 | // datamodel: The datamodel element. 43 | // on_entry: Executable block for onentry. 44 | // on_exit: Executable block for onexit. 45 | State(const string& id, bool is_final, bool is_parallel, 46 | const ExecutableContent* datamodel, const ExecutableContent* on_entry, 47 | const ExecutableContent* on_exit); 48 | State(const State&) = delete; 49 | State& operator=(const State&) = delete; 50 | 51 | std::vector* mutable_transitions() { 52 | return &transitions_; 53 | } 54 | 55 | // Add the given state as a child state and set this as the parent. 56 | void AddChild(State* state) { 57 | child_states_.push_back(state); 58 | state->SetParent(this); 59 | } 60 | 61 | // Add all the given child states as children. 62 | void AddChildren(const std::vector& child_states); 63 | 64 | string id() const { return id_; } 65 | bool IsFinal() const { return is_final_; } 66 | bool IsParallel() const { return is_parallel_; } 67 | const std::vector& GetTransitions() const { 68 | return transitions_; 69 | } 70 | 71 | const Transition* GetInitialTransition() const { return initial_transition_; } 72 | // Sets the transition and returns false if IsFinal() or 73 | // 'initial_transition->GetSourceState() != this'. 74 | bool SetInitialTransition(const Transition* initial_transition); 75 | 76 | void SetParent(const State* parent) { parent_ = parent; } 77 | const State* GetParent() const { return parent_; } 78 | const std::vector& GetChildren() const { return child_states_; } 79 | bool IsAtomic() const { return child_states_.empty(); } 80 | bool IsCompound() const { return !IsAtomic() && !IsParallel(); } 81 | const ExecutableContent* GetDatamodelBlock() const { return datamodel_; } 82 | const ExecutableContent* GetOnEntry() const { return on_entry_; } 83 | const ExecutableContent* GetOnExit() const { return on_exit_; } 84 | 85 | private: 86 | const string id_; 87 | const bool is_final_; 88 | const bool is_parallel_; 89 | const State* parent_; 90 | const Transition* initial_transition_; 91 | std::vector transitions_; 92 | std::vector child_states_; 93 | const ExecutableContent* datamodel_; 94 | const ExecutableContent* on_entry_; 95 | const ExecutableContent* on_exit_; 96 | }; 97 | 98 | } // namespace model 99 | } // namespace state_chart 100 | 101 | #endif // STATE_CHART_INTERNAL_MODEL_STATE_H_ 102 | -------------------------------------------------------------------------------- /statechart/internal/model/str_or_expr.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/str_or_expr.h" 16 | 17 | #include "statechart/internal/datamodel.h" 18 | 19 | namespace state_chart { 20 | namespace model { 21 | 22 | StrOrExpr::StrOrExpr(const string& str, const string& expr) { 23 | is_expr_ = !expr.empty(); 24 | value_ = is_expr_ ? expr : str; 25 | } 26 | 27 | bool StrOrExpr::Evaluate(const Datamodel* datamodel, string* result) const { 28 | if (is_expr_) { 29 | return datamodel->EvaluateStringExpression(value_, result); 30 | } 31 | *result = value_; 32 | return true; 33 | } 34 | 35 | } // namespace model 36 | } // namespace state_chart 37 | -------------------------------------------------------------------------------- /statechart/internal/model/str_or_expr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_STR_OR_EXPR_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_STR_OR_EXPR_H_ 22 | 23 | #include 24 | 25 | #include "statechart/platform/types.h" 26 | 27 | namespace state_chart { 28 | class Datamodel; 29 | namespace model { 30 | 31 | // A struct used to indicate to the compiler that a string is of an expression 32 | // type. This allows StrOrExpr constructors to be overloaded. 33 | struct Expr { 34 | string expr; 35 | Expr(const string& expr) // NOLINT(runtime/explicit) 36 | : expr(expr) {} 37 | }; 38 | 39 | // A convenience class used to represent variables that can either be a static 40 | // string or an expression that evaluates to a string type in the datamodel. 41 | class StrOrExpr { 42 | public: 43 | // Constructor that creates a static string. 44 | StrOrExpr(const string& str) // NOLINT(runtime/explicit) 45 | : is_expr_(false), 46 | value_(str) {} 47 | 48 | // Constructor that creates a static string from a c-string. 49 | StrOrExpr(const char* cstr) // NOLINT(runtime/explicit) 50 | : StrOrExpr(string(cstr)) {} 51 | 52 | // Constructor that creates an expression. 53 | StrOrExpr(const Expr& expr) // NOLINT(runtime/explicit) 54 | : is_expr_(true), 55 | value_(expr.expr) {} 56 | 57 | // Constructs a StrOrExpr from 'str' and 'expr' parameters of which only one 58 | // should be non-empty. 59 | StrOrExpr(const string& str, const string& expr); 60 | 61 | // Sets 'result' to the string if this is a static string, or a string 62 | // result of the expression evaluated in the datamodel. 63 | // Returns false if evaluation failed in the datamodel. 64 | bool Evaluate(const Datamodel* datamodel, string* result) const; 65 | 66 | bool IsEmpty() const { return value_.empty(); } 67 | const string& Value() const { return value_; } 68 | 69 | private: 70 | bool is_expr_; 71 | string value_; 72 | }; 73 | 74 | } // namespace model 75 | } // namespace state_chart 76 | 77 | #endif // STATE_CHART_INTERNAL_MODEL_STR_OR_EXPR_H_ 78 | -------------------------------------------------------------------------------- /statechart/internal/model/str_or_expr_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/str_or_expr.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "statechart/internal/testing/mock_datamodel.h" 21 | 22 | namespace state_chart { 23 | namespace model { 24 | namespace { 25 | 26 | using ::testing::_; 27 | using ::testing::Return; 28 | using ::testing::SetArgPointee; 29 | 30 | TEST(StrOrExprTest, TestStrOrExpr) { 31 | MockDatamodel datamodel; 32 | string result; 33 | 34 | LOG(INFO) << "Test that string is created and it is not evaluated."; 35 | StrOrExpr str1("str1"); 36 | EXPECT_CALL(datamodel, EvaluateStringExpression(_, _)).Times(0); 37 | EXPECT_TRUE(str1.Evaluate(&datamodel, &result)); 38 | EXPECT_EQ("str1", result); 39 | 40 | LOG(INFO) << "Test that an expression is created and evaluated."; 41 | StrOrExpr expr1(Expr("expr1")); 42 | EXPECT_CALL(datamodel, EvaluateStringExpression("expr1", _)) 43 | .WillOnce(DoAll(SetArgPointee<1>("result1"), Return(true))); 44 | EXPECT_TRUE(expr1.Evaluate(&datamodel, &result)); 45 | EXPECT_EQ("result1", result); 46 | 47 | LOG(INFO) << "Test error in expression evaluation."; 48 | StrOrExpr expr2(Expr("malformed")); 49 | EXPECT_CALL(datamodel, EvaluateStringExpression("malformed", _)) 50 | .WillOnce(Return(false)); 51 | EXPECT_FALSE(expr2.Evaluate(&datamodel, &result)); 52 | } 53 | 54 | } // namespace 55 | } // namespace model 56 | } // namespace state_chart 57 | -------------------------------------------------------------------------------- /statechart/internal/model/transition.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/transition.h" 16 | 17 | #include 18 | 19 | #include "statechart/internal/datamodel.h" 20 | #include "statechart/internal/model/state.h" 21 | #include "statechart/internal/runtime.h" 22 | #include "statechart/internal/utility.h" 23 | #include "absl/strings/str_cat.h" 24 | #include "absl/strings/str_join.h" 25 | #include "absl/strings/substitute.h" 26 | 27 | namespace state_chart { 28 | namespace model { 29 | 30 | Transition::Transition(const State* source, 31 | const std::vector& targets, 32 | const std::vector& events, 33 | const string& cond_expr, bool is_internal, 34 | const ExecutableContent* executable) 35 | : source_(source), 36 | targets_(targets), 37 | events_(events), 38 | cond_expr_(cond_expr), 39 | is_internal_(is_internal), 40 | executable_(executable) {} 41 | 42 | // virtual 43 | bool Transition::EvaluateCondition(Runtime* runtime) const { 44 | if (cond_expr_.empty()) { 45 | return true; 46 | } 47 | bool result = false; 48 | if (!runtime->datamodel().EvaluateBooleanExpression(cond_expr_, &result)) { 49 | runtime->EnqueueExecutionError( 50 | absl::StrCat("'Transition' condition evaluation failed: ", cond_expr_)); 51 | return false; 52 | } 53 | return result; 54 | } 55 | 56 | string Transition::DebugString() const { 57 | std::vector target_ids(targets_.size()); 58 | for (unsigned int i = 0; i < targets_.size(); ++i) { 59 | target_ids.push_back(targets_[i]->id()); 60 | } 61 | return absl::Substitute("$0 --> [$1] : events = [$2], cond = <$3>", 62 | GetSourceState()->id(), 63 | absl::StrJoin(target_ids, ","), 64 | absl::StrJoin(GetEvents(), " "), GetCondition()); 65 | } 66 | 67 | } // namespace model 68 | } // namespace state_chart 69 | -------------------------------------------------------------------------------- /statechart/internal/model/transition.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // IWYU pragma: private, include "src/internal/model/model.h" 18 | // IWYU pragma: friend src/internal/model/* 19 | 20 | #ifndef STATE_CHART_INTERNAL_MODEL_TRANSITION_H_ 21 | #define STATE_CHART_INTERNAL_MODEL_TRANSITION_H_ 22 | 23 | #include 24 | #include 25 | 26 | #include "statechart/platform/types.h" 27 | #include "statechart/internal/model/model_element.h" 28 | 29 | namespace state_chart { 30 | class Runtime; 31 | namespace model { 32 | class ExecutableContent; 33 | class State; 34 | } // namespace model 35 | } // namespace state_chart 36 | 37 | namespace state_chart { 38 | namespace model { 39 | 40 | // Basic Transition implementation. 41 | class Transition : public ModelElement { 42 | public: 43 | // Params: 44 | // source The source state where this transition is nested in, may be 45 | // nullptr for transitions of initial elements. 46 | // targets The target states for transition. 47 | // events The list of event descriptors to transit on. 48 | // is_internal True if the type of transition is internal, otherwise the type 49 | // is external. 50 | // executable Executable content body of the transition. 51 | Transition(const State* source, const std::vector& targets, 52 | const std::vector& events, const string& cond_expr, 53 | bool is_internal, const ExecutableContent* executable); 54 | 55 | Transition(const Transition&) = delete; 56 | Transition& operator=(const Transition&) = delete; 57 | 58 | const State* GetSourceState() const { return source_; } 59 | const std::vector& GetTargetStates() const { return targets_; } 60 | const std::vector& GetEvents() const { return events_; } 61 | const string& GetCondition() const { return cond_expr_; } 62 | bool IsInternal() const { return is_internal_; } 63 | const ExecutableContent* GetExecutable() const { return executable_; } 64 | 65 | // Evaluates the condition. Returns true if the condition is satisfied or if 66 | // there is no condition on this transition. Returns false if the condition is 67 | // unsatisfied or an error occurred while evaluating the condition. An 68 | // 'error.execution' event will be enqueued in 'runtime' when evaluation 69 | // fails. 70 | virtual bool EvaluateCondition(Runtime* runtime) const; 71 | 72 | // A string for representing the transition. 73 | string DebugString() const; 74 | 75 | private: 76 | const State* source_; 77 | const std::vector targets_; 78 | const std::vector events_; 79 | // The condition expression, empty string for none. 80 | const string cond_expr_; 81 | const bool is_internal_; 82 | const ExecutableContent* executable_; 83 | }; 84 | 85 | } // namespace model 86 | } // namespace state_chart 87 | 88 | #endif // STATE_CHART_INTERNAL_MODEL_TRANSITION_H_ 89 | -------------------------------------------------------------------------------- /statechart/internal/model/transition_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model/transition.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "statechart/internal/testing/mock_runtime.h" 21 | 22 | namespace state_chart { 23 | namespace model { 24 | namespace { 25 | 26 | using ::testing::NotNull; 27 | using ::testing::_; 28 | 29 | TEST(TransitionTest, EvaluateCondition) { 30 | MockRuntime runtime; 31 | 32 | // Test transition without a condition. 33 | Transition transition_no_condition(nullptr, {}, {}, "", false, nullptr); 34 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), 35 | EvaluateBooleanExpression("", _)) 36 | .Times(0); 37 | EXPECT_TRUE(transition_no_condition.EvaluateCondition(&runtime)); 38 | 39 | // Test transition with a condition that is true. 40 | Transition transition_with_true(nullptr, {}, {}, "true", false, nullptr); 41 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), 42 | EvaluateBooleanExpression("true", NotNull())) 43 | .WillOnce(ReturnEvaluationResult(true)); 44 | EXPECT_TRUE(transition_with_true.EvaluateCondition(&runtime)); 45 | 46 | // Test transition with a condition that is false. 47 | Transition transition_with_false(nullptr, {}, {}, "false", false, nullptr); 48 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), 49 | EvaluateBooleanExpression("false", NotNull())) 50 | .WillOnce(ReturnEvaluationResult(false)); 51 | EXPECT_FALSE(transition_with_false.EvaluateCondition(&runtime)); 52 | 53 | // Test transition with a condition evaluation error. 54 | Transition transition_with_error(nullptr, {}, {}, "error", false, nullptr); 55 | EXPECT_CALL(runtime.GetDefaultMockDatamodel(), 56 | EvaluateBooleanExpression("error", NotNull())) 57 | .WillOnce(ReturnEvaluationError()); 58 | EXPECT_CALL(runtime, EnqueueInternalEvent("error.execution", _)); 59 | EXPECT_FALSE(transition_with_error.EvaluateCondition(&runtime)); 60 | } 61 | 62 | } // namespace 63 | } // namespace model 64 | } // namespace state_chart 65 | -------------------------------------------------------------------------------- /statechart/internal/model_builder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_MODEL_BUILDER_H_ 18 | #define STATE_CHART_INTERNAL_MODEL_BUILDER_H_ 19 | 20 | #include 21 | #include 22 | 23 | #include "statechart/platform/protobuf.h" 24 | #include "statechart/platform/types.h" 25 | #include "statechart/proto/state_chart.pb.h" 26 | 27 | namespace state_chart { 28 | 29 | class Model; 30 | 31 | namespace model { 32 | class Assign; 33 | class ExecutableContent; 34 | class ForEach; 35 | class If; 36 | class Log; 37 | class ModelElement; 38 | class Raise; 39 | class Send; 40 | class State; 41 | class Transition; 42 | } // namespace model 43 | 44 | // This class builds a Model from a StateChart proto, and performs validation 45 | // that the StateChart is well formed. The Model can be used by a StateMachine 46 | // instance, and describes the semantics of a specific state machine. 47 | // 48 | // Typical usage: 49 | // 50 | // const StateChart my_config = ...; 51 | // 52 | // ModelBuilder builder(my_config); 53 | // builder.Build(); 54 | // 55 | // Model* my_model = builder.CreateModelAndReset(); 56 | // 57 | // TODO(thatguy): Add error checking ability after Build(). 58 | class ModelBuilder { 59 | public: 60 | // Returns a new model instance. Caller takes ownership. 61 | // Returns nullptr if error occurred when building model. 62 | static Model* CreateModelOrNull(const config::StateChart& state_chart); 63 | 64 | explicit ModelBuilder(const config::StateChart& state_chart); 65 | virtual ~ModelBuilder(); 66 | 67 | // Parses and validates 'state_chart' and creates all model objects. Must be 68 | // called before CreateModelAndReset(). 69 | bool Build(); 70 | 71 | // Returns a new Model instance. Caller takes ownership. 72 | Model* CreateModelAndReset(); 73 | 74 | protected: 75 | // Returns the internal state of this object to that at instantiation time. 76 | void Reset(); 77 | 78 | // Build various instances of ExecutableContent. 79 | 80 | // Returns nullptr when 'elements.size() == 0', i.e., empty executable blocks 81 | // are denoted by nullptr. 82 | virtual model::ExecutableContent* BuildExecutableBlock( 83 | const proto2::RepeatedPtrField& elements); 84 | 85 | virtual model::ExecutableContent* BuildExecutableContent( 86 | const config::ExecutableElement& element); 87 | 88 | virtual model::ExecutableContent* BuildDataModelBlock( 89 | const config::DataModel& datamodel); 90 | 91 | virtual model::Raise* BuildRaise(const config::Raise& raise_proto); 92 | virtual model::Log* BuildLog(const config::Log& log_proto); 93 | virtual model::Assign* BuildAssign(const config::Assign& assign_proto); 94 | virtual model::Send* BuildSend(const config::Send& send_proto); 95 | virtual model::If* BuildIf(const config::If& if_proto); 96 | virtual model::ForEach* BuildForEach(const config::ForEach& for_each_proto); 97 | 98 | // Build State-ful objects. 99 | virtual model::State* BuildState(const config::StateElement& state_proto); 100 | 101 | // Transitions. 102 | virtual bool BuildTransitionsForState(model::State* state); 103 | 104 | // Member variables. 105 | const config::StateChart state_chart_; 106 | 107 | const model::Transition* initial_transition_ = nullptr; 108 | model::ExecutableContent* datamodel_block_ = nullptr; 109 | std::vector top_level_states_; 110 | 111 | std::map states_map_; 112 | std::map states_config_map_; 113 | std::vector all_elements_; 114 | }; 115 | 116 | } // namespace state_chart 117 | 118 | #endif // STATE_CHART_INTERNAL_MODEL_BUILDER_H_ 119 | -------------------------------------------------------------------------------- /statechart/internal/model_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/model.h" 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | namespace state_chart { 23 | namespace { 24 | 25 | struct EventTestCase { 26 | std::vector events; 27 | string event_name; 28 | bool result; 29 | }; 30 | 31 | TEST(Model, EventsMatchTest) { 32 | const EventTestCase kTestCases[] = { 33 | // Single matching. 34 | {{"A"}, "A", true}, 35 | {{"B"}, "A", false}, 36 | // Multiple matching. 37 | {{"A", "B"}, "A", true}, 38 | {{"A", "B"}, "B", true}, 39 | {{"A", "B", "D"}, "C", false}, 40 | // Hierarchical matching. 41 | {{"A"}, "A.A1", true}, 42 | {{"A.A1"}, "A.A1.AA1", true}, 43 | {{"A.A1"}, "A.A2", false}, 44 | // Multiple hierarchies. 45 | {{"A", "B", "C"}, "A.A1.AA1", true}, 46 | {{"A", "B", "C"}, "C.C1.CC1", true}, 47 | {{"A", "B", "C"}, "E.E1.EE1", false}, 48 | {{"A.A1", "B", "C.C1.CC1"}, "A.A1.AA1", true}, 49 | {{"A.A1", "B", "C.C1.CC1"}, "C.C1.CC2", false}, 50 | {{"A.A1", "B", "C.C1.CC1"}, "B.B1.BB1", true}, 51 | // Star matches everything. 52 | {{"*"}, "A", true}, 53 | {{"B", "*"}, "A", true}, 54 | {{"A.A1", "*", "C.C1.CC1"}, "C.C1.CC2", true}, 55 | }; 56 | 57 | for (const auto& test_case : kTestCases) { 58 | EXPECT_EQ(test_case.result, 59 | Model::EventMatches(test_case.event_name, test_case.events)); 60 | } 61 | } 62 | 63 | } // namespace 64 | } // namespace state_chart 65 | -------------------------------------------------------------------------------- /statechart/internal/runtime.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/runtime.h" 16 | 17 | #include "statechart/internal/utility.h" 18 | #include "absl/strings/str_cat.h" 19 | 20 | namespace state_chart { 21 | 22 | void Runtime::EnqueueExecutionError(const string& error_msg) { 23 | EnqueueInternalEvent("error.execution", 24 | MakeJSONError(absl::StrCat("[datamodel] ", error_msg))); 25 | } 26 | 27 | } // namespace state_chart 28 | -------------------------------------------------------------------------------- /statechart/internal/runtime.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_RUNTIME_H_ 18 | #define STATE_CHART_INTERNAL_RUNTIME_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "statechart/platform/types.h" 25 | #include "statechart/proto/state_machine_context.pb.h" 26 | 27 | namespace state_chart { 28 | class Datamodel; 29 | class EventDispatcher; 30 | namespace model { 31 | class State; 32 | } // namespace model 33 | } // namespace state_chart 34 | 35 | namespace state_chart { 36 | 37 | // Runtime holds all runtime information (state) of an executing state machine. 38 | class Runtime { 39 | public: 40 | virtual ~Runtime() {} 41 | 42 | // Get the list of currently active states. 43 | virtual std::set GetActiveStates() const = 0; 44 | 45 | // Returns true iff the state with id 'state_id' is currently active. 46 | virtual bool IsActiveState(const string& state_id) const = 0; 47 | 48 | // Add an active state to the active states. 49 | // Does nothing if state is already an active state. 50 | virtual void AddActiveState(const model::State* state) = 0; 51 | 52 | // Erase an active state from the list of active states. 53 | // Does nothing if the state is not an active state. 54 | virtual void EraseActiveState(const model::State* state) = 0; 55 | 56 | // Flag to indicate state machine is running. 57 | virtual bool IsRunning() const = 0; 58 | virtual void SetRunning(bool is_running) = 0; 59 | 60 | // Returns true if the internal event queue is not empty. 61 | virtual bool HasInternalEvent() const = 0; 62 | 63 | // Returns a pair of event id and payload. 64 | // Returns a pair of empty strings if !HasInternalEvent(). 65 | // TODO(qplau): Event should be a struct or class to hold other information 66 | // such as 'type'. 67 | virtual std::pair DequeueInternalEvent() = 0; 68 | 69 | // Append an internal event to a FIFO queue. 70 | virtual void EnqueueInternalEvent(const string& event, 71 | const string& payload) = 0; 72 | 73 | // Returns the Datamodel in use for this instance. 74 | virtual Datamodel* mutable_datamodel() = 0; 75 | virtual const Datamodel& datamodel() const = 0; 76 | 77 | // Returns the ListenerEventDispatcher in use for this instance. 78 | virtual EventDispatcher* GetEventDispatcher() = 0; 79 | 80 | // Clears all data in the runtime, including the data in the datamodel. 81 | virtual void Clear() = 0; 82 | 83 | // Debug string of the data contained in the runtime. 84 | virtual string DebugString() const = 0; 85 | 86 | // Enqueue an "error.execution" event with a Json payload given the error 87 | // string. 'error_msg' will be prepended with "[datamodel] ". 88 | void EnqueueExecutionError(const string& error_msg); 89 | 90 | // Serializes the internal state of the runtime. 91 | virtual StateMachineContext::Runtime Serialize() const = 0; 92 | }; 93 | 94 | } // namespace state_chart 95 | 96 | #endif // STATE_CHART_INTERNAL_RUNTIME_H_ 97 | -------------------------------------------------------------------------------- /statechart/internal/runtime_impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_RUNTIME_IMPL_H_ 18 | #define STATE_CHART_INTERNAL_RUNTIME_IMPL_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "statechart/platform/types.h" 27 | #include "statechart/internal/event_dispatcher.h" 28 | #include "statechart/internal/runtime.h" 29 | #include "statechart/proto/state_machine_context.pb.h" 30 | 31 | namespace state_chart { 32 | class Datamodel; 33 | namespace model { 34 | class State; 35 | } // namespace model 36 | } // namespace state_chart 37 | 38 | namespace state_chart { 39 | 40 | class RuntimeImpl : public Runtime { 41 | public: 42 | // Creates a Runtime with given 'datamodel'. 43 | // Returns nullptr, if datamodel is null. 44 | static std::unique_ptr Create(std::unique_ptr datamodel); 45 | RuntimeImpl(const RuntimeImpl&) = delete; 46 | RuntimeImpl& operator=(const RuntimeImpl&) = delete; 47 | ~RuntimeImpl() override = default; 48 | 49 | // Get the list of currently active states. 50 | std::set GetActiveStates() const override { 51 | return active_states_; 52 | } 53 | 54 | // Returns true iff the state with id 'state_id' is currently active. 55 | bool IsActiveState(const string& state_id) const override; 56 | 57 | // Add an active state to the active states. 58 | // Does nothing if state is already an active state. 59 | void AddActiveState(const model::State* state) override; 60 | 61 | // Erase an active state from the list of active states. 62 | // Does nothing if the state is not an active state. 63 | void EraseActiveState(const model::State* state) override; 64 | 65 | // Flag to indicate state machine is running. 66 | bool IsRunning() const override { return is_running_; } 67 | void SetRunning(bool is_running) override { is_running_ = is_running; } 68 | 69 | // Returns true if the internal event queue is not empty. 70 | bool HasInternalEvent() const override; 71 | 72 | // Returns a pair of event id and payload. 73 | // Returns a pair of empty strings if !HasInternalEvent(). 74 | std::pair DequeueInternalEvent() override; 75 | 76 | // Append an internal event to a FIFO queue. 77 | void EnqueueInternalEvent(const string& event, 78 | const string& payload) override; 79 | 80 | // Returns the Datamodel in use for this instance. 81 | Datamodel* mutable_datamodel() override { return datamodel_.get(); } 82 | const Datamodel& datamodel() const override { return *datamodel_; } 83 | EventDispatcher* GetEventDispatcher() override { 84 | return &listener_event_dispatcher_; 85 | } 86 | 87 | void Clear() override; 88 | 89 | // Returns a string describing only the active state ids and internal event 90 | // queue. 91 | string DebugString() const override; 92 | 93 | // Serializes the internal state of the runtime. 94 | StateMachineContext::Runtime Serialize() const override; 95 | 96 | private: 97 | // Creates a RuntimeImpl from given 'datamodel'. 98 | // 'datamodel' must be non null. 99 | explicit RuntimeImpl(std::unique_ptr datamodel); 100 | 101 | std::set active_states_; 102 | 103 | bool is_running_ = false; 104 | 105 | // FIFO queue. 106 | std::list> internal_events_; 107 | 108 | std::unique_ptr datamodel_; 109 | 110 | EventDispatcher listener_event_dispatcher_; 111 | }; 112 | 113 | } // namespace state_chart 114 | 115 | #endif // STATE_CHART_INTERNAL_RUNTIME_IMPL_H_ 116 | -------------------------------------------------------------------------------- /statechart/internal/state_machine_impl.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/state_machine_impl.h" 16 | 17 | #include "absl/memory/memory.h" 18 | #include "statechart/internal/event_dispatcher.h" 19 | #include "statechart/internal/executor.h" 20 | #include "statechart/internal/light_weight_datamodel.h" 21 | #include "statechart/internal/model.h" 22 | #include "statechart/internal/runtime.h" 23 | 24 | using ::absl::WrapUnique; 25 | 26 | namespace state_chart { 27 | class StateMachineListener; 28 | } // namespace state_chart 29 | 30 | namespace state_chart { 31 | 32 | // static 33 | std::unique_ptr StateMachineImpl::Create( 34 | const Executor* executor, const Model* model, 35 | std::unique_ptr runtime) { 36 | RETURN_NULL_IF(executor == nullptr || model == nullptr || runtime == nullptr); 37 | return WrapUnique(new StateMachineImpl(executor, model, std::move(runtime))); 38 | } 39 | 40 | StateMachineImpl::StateMachineImpl(const Executor* executor, 41 | const Model* model, 42 | std::unique_ptr runtime) 43 | : executor_(executor), model_(model), runtime_(std::move(runtime)) {} 44 | 45 | // override 46 | void StateMachineImpl::Start() { 47 | executor_->Start(model_, runtime_.get()); 48 | } 49 | 50 | // override 51 | void StateMachineImpl::SendEvent(const string& event, const string& payload) { 52 | executor_->SendEvent(model_, runtime_.get(), event, payload); 53 | } 54 | 55 | // override 56 | void StateMachineImpl::AddListener(StateMachineListener* listener) { 57 | runtime_->GetEventDispatcher()->AddListener(listener); 58 | } 59 | 60 | // override 61 | const Runtime& StateMachineImpl::GetRuntime() const { 62 | return *runtime_; 63 | } 64 | 65 | // override 66 | const Model& StateMachineImpl::GetModel() const { 67 | return *model_; 68 | } 69 | 70 | } // namespace state_chart 71 | -------------------------------------------------------------------------------- /statechart/internal/state_machine_impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_STATE_MACHINE_IMPL_H_ 18 | #define STATE_CHART_INTERNAL_STATE_MACHINE_IMPL_H_ 19 | 20 | #include 21 | #include 22 | 23 | #include "statechart/state_machine.h" 24 | 25 | namespace state_chart { 26 | 27 | class Executor; 28 | class Model; 29 | class Runtime; 30 | class StateMachineListener; 31 | 32 | class StateMachineImpl : public StateMachine { 33 | public: 34 | // Returns a StateMachine generated from given 'executor', 'model' & 35 | // 'runtime'. 36 | // All input params must be non-null. The executor and the model must outlive 37 | // the constructed StateMachineImpl instance. 38 | // Returns nullptr if StateMachine cannot be created. 39 | static std::unique_ptr Create(const Executor* executor, 40 | const Model* model, 41 | std::unique_ptr runtime); 42 | StateMachineImpl(const StateMachineImpl&) = delete; 43 | StateMachineImpl& operator=(const StateMachineImpl&) = delete; 44 | ~StateMachineImpl() override = default; 45 | 46 | void Start() override; 47 | 48 | void SendEvent(const string& event, const string& payload) override; 49 | 50 | void AddListener(StateMachineListener* listener) override; 51 | 52 | const Runtime& GetRuntime() const override; 53 | 54 | const Model& GetModel() const override; 55 | 56 | private: 57 | StateMachineImpl(const Executor* executor, const Model* model, 58 | std::unique_ptr runtime); 59 | 60 | const Executor* const executor_; // Not owned. 61 | const Model* const model_; // Not owned. 62 | std::unique_ptr runtime_; 63 | }; 64 | 65 | } // namespace state_chart 66 | 67 | #endif // STATE_CHART_INTERNAL_STATE_MACHINE_IMPL_H_ 68 | -------------------------------------------------------------------------------- /statechart/internal/state_machine_logger.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/state_machine_logger.h" 16 | 17 | #include "absl/strings/str_join.h" 18 | #include "absl/strings/substitute.h" 19 | #include "statechart/internal/datamodel.h" 20 | #include "statechart/internal/model/model.h" 21 | #include "statechart/internal/runtime.h" 22 | #include "statechart/platform/types.h" 23 | 24 | namespace state_chart { 25 | 26 | namespace { 27 | 28 | // Returns a string containing the state chart name and session id. 29 | string GetStateChartId(const Runtime* runtime) { 30 | string name, id; 31 | const auto& datamodel = runtime->datamodel(); 32 | datamodel.EvaluateStringExpression("_name", &name); 33 | datamodel.EvaluateStringExpression("_sessionid", &id); 34 | return absl::Substitute("[$0][$1]", id, name); 35 | } 36 | 37 | } // namespace 38 | 39 | void StateMachineLogger::OnStateEntered(const Runtime* runtime, 40 | const model::State* state) { 41 | VLOG(1) << absl::Substitute("$0 State entered: $1", GetStateChartId(runtime), 42 | state->id()); 43 | } 44 | 45 | void StateMachineLogger::OnStateExited(const Runtime* runtime, 46 | const model::State* state) { 47 | VLOG(1) << absl::Substitute("$0 State exited: $1", GetStateChartId(runtime), 48 | state->id()); 49 | } 50 | 51 | void StateMachineLogger::OnTransitionFollowed( 52 | const Runtime* runtime, const model::Transition* transition) { 53 | std::vector targets; 54 | for (const auto* state : transition->GetTargetStates()) { 55 | targets.emplace_back(state->id()); 56 | } 57 | VLOG(1) << absl::Substitute( 58 | "$0 Transition followed: cond=\"$1\", source=[$2], targets=[$3]", 59 | GetStateChartId(runtime), transition->GetCondition(), 60 | transition->GetSourceState() == nullptr 61 | ? "null" 62 | : transition->GetSourceState()->id(), 63 | absl::StrJoin(targets, ", ")); 64 | } 65 | 66 | void StateMachineLogger::OnSendEvent(const Runtime* runtime, 67 | const string& event, const string& target, 68 | const string& type, const string& id, 69 | const string& data) { 70 | VLOG(1) << absl::Substitute( 71 | "$0 Send event: event=$1, target=$2, type=$3, id=$4, data=$5", 72 | GetStateChartId(runtime), event, target, type, id, data); 73 | } 74 | 75 | } // namespace state_chart 76 | -------------------------------------------------------------------------------- /statechart/internal/state_machine_logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_STATE_MACHINE_LOGGER_H_ 18 | #define STATE_CHART_INTERNAL_STATE_MACHINE_LOGGER_H_ 19 | 20 | #include 21 | 22 | #include "statechart/platform/types.h" 23 | #include "statechart/state_machine_listener.h" 24 | 25 | namespace state_chart { 26 | class Runtime; 27 | namespace model { 28 | class State; 29 | } // namespace model 30 | } // namespace state_chart 31 | 32 | namespace state_chart { 33 | 34 | // Logs state machine changes using VLOG(1). 35 | class StateMachineLogger : public StateMachineListener { 36 | public: 37 | StateMachineLogger() = default; 38 | StateMachineLogger(const StateMachineLogger&) = delete; 39 | StateMachineLogger& operator=(const StateMachineLogger&) = delete; 40 | ~StateMachineLogger() override = default; 41 | 42 | void OnStateEntered(const Runtime* runtime, 43 | const model::State* state) override; 44 | 45 | void OnStateExited(const Runtime* runtime, 46 | const model::State* state) override; 47 | 48 | void OnTransitionFollowed(const Runtime* runtime, 49 | const model::Transition* transition) override; 50 | 51 | void OnSendEvent(const Runtime* runtime, const string& event, 52 | const string& target, const string& type, const string& id, 53 | const string& data) override; 54 | }; 55 | 56 | } // namespace state_chart 57 | 58 | #endif // STATE_CHART_INTERNAL_STATE_MACHINE_LOGGER_H_ 59 | -------------------------------------------------------------------------------- /statechart/internal/testing/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The StateChart Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Description: 16 | # A set of utilities needed for testing StateChart model. 17 | 18 | package(default_visibility = [ 19 | "//statechart:__subpackages__", 20 | ]) 21 | 22 | cc_library( 23 | name = "delegating_mock_executor", 24 | testonly = 1, 25 | hdrs = ["delegating_mock_executor.h"], 26 | deps = [ 27 | "//statechart/internal:executor", 28 | "@com_google_googletest//:gtest", 29 | ], 30 | ) 31 | 32 | proto_library( 33 | name = "json_value_coder_test_proto", 34 | srcs = ["json_value_coder_test.proto"], 35 | # deps = ["//net/proto2/util/proto:json_format_proto"], 36 | ) 37 | 38 | cc_proto_library( 39 | name = "json_value_coder_test_cc_proto", 40 | deps = ["json_value_coder_test_proto"], 41 | ) 42 | 43 | cc_library( 44 | name = "mock_executable_content", 45 | testonly = 1, 46 | hdrs = ["mock_executable_content.h"], 47 | deps = [ 48 | "//statechart/internal/model:executable_content", 49 | "@com_google_googletest//:gtest", 50 | ], 51 | ) 52 | 53 | cc_library( 54 | name = "mock_event_dispatcher", 55 | testonly = 1, 56 | hdrs = ["mock_event_dispatcher.h"], 57 | deps = [ 58 | "//statechart/internal:event_dispatcher", 59 | "//statechart/platform:types", 60 | "@com_google_googletest//:gtest", 61 | ], 62 | ) 63 | 64 | cc_library( 65 | name = "mock_function_dispatcher", 66 | testonly = 1, 67 | hdrs = ["mock_function_dispatcher.h"], 68 | deps = [ 69 | "//statechart/internal:function_dispatcher", 70 | "//statechart/platform:types", 71 | "@com_google_googletest//:gtest", 72 | ], 73 | ) 74 | 75 | cc_library( 76 | name = "mock_datamodel", 77 | testonly = 1, 78 | hdrs = ["mock_datamodel.h"], 79 | deps = [ 80 | "//statechart:logging", 81 | "//statechart/internal:datamodel", 82 | "//statechart/internal:runtime", 83 | "@com_google_absl//absl/strings", 84 | "@com_google_glog//:glog", 85 | "@com_google_googletest//:gtest", 86 | ], 87 | ) 88 | 89 | cc_library( 90 | name = "mock_model", 91 | testonly = 1, 92 | hdrs = ["mock_model.h"], 93 | deps = [ 94 | ":mock_transition", 95 | "//statechart/internal:model", 96 | "//statechart/platform:types", 97 | "@com_google_absl//absl/algorithm:container", 98 | "@com_google_googletest//:gtest", 99 | ], 100 | ) 101 | 102 | cc_library( 103 | name = "mock_runtime", 104 | testonly = 1, 105 | hdrs = ["mock_runtime.h"], 106 | deps = [ 107 | ":mock_datamodel", 108 | ":mock_event_dispatcher", 109 | "//statechart/internal:datamodel", 110 | "//statechart/internal:event_dispatcher", 111 | "//statechart/internal:runtime", 112 | "//statechart/proto:state_machine_context_cc_proto", 113 | "@com_google_googletest//:gtest", 114 | ], 115 | ) 116 | 117 | cc_library( 118 | name = "mock_state", 119 | testonly = 1, 120 | hdrs = ["mock_state.h"], 121 | deps = [ 122 | "//statechart/internal/model:state", 123 | "//statechart/platform:types", 124 | "@com_google_googletest//:gtest", 125 | ], 126 | ) 127 | 128 | cc_library( 129 | name = "mock_transition", 130 | testonly = 1, 131 | hdrs = ["mock_transition.h"], 132 | deps = [ 133 | "//statechart/internal/model:transition", 134 | "//statechart/platform:types", 135 | "@com_google_googletest//:gtest", 136 | ], 137 | ) 138 | 139 | cc_library( 140 | name = "state_chart_builder", 141 | testonly = 1, 142 | hdrs = ["state_chart_builder.h"], 143 | deps = [ 144 | "//statechart/platform:protobuf", 145 | "//statechart/platform:types", 146 | "//statechart/proto:state_chart_cc_proto", 147 | "@com_google_glog//:glog", 148 | ], 149 | ) 150 | 151 | cc_test( 152 | name = "state_chart_builder_test", 153 | srcs = ["state_chart_builder_test.cc"], 154 | deps = [ 155 | ":state_chart_builder", 156 | "//statechart/platform:protobuf", 157 | "//statechart/platform:test_util", 158 | "//statechart/platform:types", 159 | "//statechart/proto:state_chart_cc_proto", 160 | "@com_google_googletest//:gtest_main", 161 | ], 162 | ) 163 | -------------------------------------------------------------------------------- /statechart/internal/testing/json_value_coder_test.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto2"; 16 | 17 | //import "net/proto2/util/proto/json_format.proto"; 18 | 19 | package state_chart; 20 | 21 | message TestA { 22 | message TestB { 23 | optional int32 a_int32 = 1; 24 | optional int64 a_int64 = 2 [default = 10]; 25 | optional float a_float = 3; 26 | optional double a_double = 4; 27 | optional string a_string = 5; 28 | } 29 | 30 | optional bool a_bool = 1; 31 | required string a_required_string = 2; 32 | optional TestB test_b = 3; 33 | 34 | extensions 15 to 20; 35 | } 36 | 37 | message TestAExtension { 38 | optional int32 ext_value = 1; 39 | 40 | extend TestA { 41 | optional TestAExtension ext = 15 [json_name = "test_a_ext"]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /statechart/internal/testing/mock_datamodel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_TESTING_MOCK_DATAMODEL_H_ 18 | #define STATE_CHART_INTERNAL_TESTING_MOCK_DATAMODEL_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "absl/strings/str_cat.h" 29 | #include "statechart/internal/datamodel.h" 30 | #include "statechart/internal/runtime.h" 31 | #include "statechart/logging.h" 32 | 33 | ACTION_P(ReturnEvaluationResult, val) { 34 | *arg1 = val; 35 | return true; 36 | } 37 | 38 | ACTION(ReturnEvaluationError) { return false; } 39 | 40 | namespace state_chart { 41 | 42 | // A fake iterator that iterates through a vector of strings 43 | class MockIterator : public Iterator { 44 | public: 45 | explicit MockIterator(const std::vector& collection) 46 | : index_(0), collection_(collection) {} 47 | 48 | ~MockIterator() override = default; 49 | 50 | bool AtEnd() const override { return index_ >= collection_.size(); } 51 | 52 | bool Next() override { 53 | RETURN_FALSE_IF(AtEnd()); 54 | ++index_; 55 | return true; 56 | } 57 | 58 | string GetValue() const override { 59 | RETURN_VALUE_IF_MSG(AtEnd(), "", "AtEnd() is true."); 60 | return collection_[index_]; 61 | } 62 | 63 | string GetIndex() const override { return ::absl::StrCat(index_); } 64 | 65 | private: 66 | unsigned int index_; 67 | const std::vector collection_; 68 | }; 69 | 70 | class MockDatamodel : public Datamodel { 71 | public: 72 | MockDatamodel() {} 73 | MockDatamodel(const MockDatamodel&) = delete; 74 | MockDatamodel& operator=(const MockDatamodel&) = delete; 75 | virtual ~MockDatamodel() {} 76 | 77 | MOCK_CONST_METHOD1(IsDefined, bool(const string&)); 78 | MOCK_METHOD1(Declare, bool(const string&)); 79 | MOCK_METHOD2(AssignExpression, bool(const string&, const string&)); 80 | MOCK_METHOD2(AssignString, bool(const string&, const string&)); 81 | MOCK_CONST_METHOD2(EvaluateBooleanExpression, bool(const string&, bool*)); 82 | MOCK_CONST_METHOD2(EvaluateStringExpression, bool(const string&, string*)); 83 | MOCK_CONST_METHOD2(EvaluateExpression, bool(const string&, string*)); 84 | MOCK_CONST_METHOD1(EncodeParameters, string(const std::map&)); 85 | MOCK_CONST_METHOD0(DebugString, string()); 86 | MOCK_METHOD0(Clear, void()); 87 | MOCK_CONST_METHOD0(Clone, std::unique_ptr()); 88 | MOCK_CONST_METHOD0(SerializeAsString, string()); 89 | MOCK_CONST_METHOD1(EvaluateIterator, 90 | std::unique_ptr(const string&)); 91 | MOCK_METHOD1(ParseFromString, bool(const string&)); 92 | MOCK_CONST_METHOD0(GetRuntime, const Runtime*()); 93 | MOCK_METHOD1(SetRuntime, void(const Runtime*)); 94 | }; 95 | 96 | } // namespace state_chart 97 | 98 | #endif // STATE_CHART_INTERNAL_TESTING_MOCK_DATAMODEL_H_ 99 | -------------------------------------------------------------------------------- /statechart/internal/testing/mock_event_dispatcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_TESTING_MOCK_EVENT_DISPATCHER_H_ 18 | #define STATE_CHART_INTERNAL_TESTING_MOCK_EVENT_DISPATCHER_H_ 19 | 20 | #include 21 | #include 22 | 23 | #include "statechart/internal/event_dispatcher.h" 24 | #include "statechart/platform/types.h" 25 | 26 | namespace state_chart { 27 | 28 | class Runtime; 29 | 30 | namespace model { 31 | class State; 32 | class Transition; 33 | } // namespace model 34 | 35 | class MockEventDispatcher : public EventDispatcher { 36 | public: 37 | ~MockEventDispatcher() override = default; 38 | 39 | MOCK_METHOD2(NotifyStateEntered, void(const Runtime*, const model::State*)); 40 | MOCK_METHOD2(NotifyStateExited, void(const Runtime*, const model::State*)); 41 | MOCK_METHOD2(NotifyTransitionFollowed, 42 | void(const Runtime*, const model::Transition*)); 43 | 44 | MOCK_METHOD6(NotifySendEvent, 45 | void(const Runtime*, const string&, const string&, const string&, 46 | const string&, const string&)); 47 | }; 48 | 49 | } // namespace state_chart 50 | 51 | #endif // STATE_CHART_INTERNAL_TESTING_MOCK_EVENT_DISPATCHER_H_ 52 | -------------------------------------------------------------------------------- /statechart/internal/testing/mock_executable_content.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_TESTING_MOCK_EXECUTABLE_CONTENT_H_ 18 | #define STATE_CHART_INTERNAL_TESTING_MOCK_EXECUTABLE_CONTENT_H_ 19 | 20 | #include "statechart/internal/model/executable_content.h" 21 | 22 | #include 23 | 24 | namespace state_chart { 25 | 26 | class Runtime; 27 | 28 | class MockExecutableContent : public model::ExecutableContent { 29 | public: 30 | MockExecutableContent() { 31 | using ::testing::_; 32 | using ::testing::Return; 33 | ON_CALL(*this, Execute(_)).WillByDefault(Return(true)); 34 | } 35 | MockExecutableContent(const MockExecutableContent&) = delete; 36 | MockExecutableContent& operator=(const MockExecutableContent&) = delete; 37 | ~MockExecutableContent() override = default; 38 | 39 | MOCK_CONST_METHOD1(Execute, bool(Runtime*)); 40 | }; 41 | 42 | } // namespace state_chart 43 | 44 | #endif // STATE_CHART_INTERNAL_TESTING_MOCK_EXECUTABLE_CONTENT_H_ 45 | -------------------------------------------------------------------------------- /statechart/internal/testing/mock_function_dispatcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_TESTING_MOCK_FUNCTION_DISPATCHER_H_ 18 | #define STATE_CHART_INTERNAL_TESTING_MOCK_FUNCTION_DISPATCHER_H_ 19 | 20 | #include 21 | 22 | #include "statechart/platform/types.h" 23 | #include "statechart/internal/function_dispatcher.h" 24 | 25 | namespace Json { 26 | class Value; 27 | } // namespace Json 28 | 29 | namespace state_chart { 30 | 31 | class MockFunctionDispatcher : public FunctionDispatcher { 32 | public: 33 | MockFunctionDispatcher() { 34 | using testing::_; 35 | using testing::Const; 36 | using testing::Return; 37 | ON_CALL(Const(*this), HasFunction(_)).WillByDefault(Return(false)); 38 | ON_CALL(*this, Execute(_, _, _)).WillByDefault(Return(false)); 39 | } 40 | MockFunctionDispatcher(const MockFunctionDispatcher&) = delete; 41 | MockFunctionDispatcher& operator=(const MockFunctionDispatcher&) = delete; 42 | ~MockFunctionDispatcher() override = default; 43 | 44 | MOCK_CONST_METHOD1(HasFunction, bool(const string&)); 45 | MOCK_METHOD3(Execute, 46 | bool(const string&, const std::vector&, 47 | Json::Value*)); 48 | }; 49 | 50 | } // namespace state_chart 51 | 52 | #endif // STATE_CHART_INTERNAL_TESTING_MOCK_FUNCTION_DISPATCHER_H_ 53 | -------------------------------------------------------------------------------- /statechart/internal/testing/mock_runtime.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_TESTING_MOCK_RUNTIME_H_ 18 | #define STATE_CHART_INTERNAL_TESTING_MOCK_RUNTIME_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include "statechart/internal/datamodel.h" 27 | #include "statechart/internal/event_dispatcher.h" 28 | #include "statechart/internal/runtime.h" 29 | #include "statechart/internal/testing/mock_datamodel.h" 30 | #include "statechart/internal/testing/mock_event_dispatcher.h" 31 | #include "statechart/proto/state_machine_context.pb.h" 32 | 33 | namespace state_chart { 34 | namespace model { 35 | class State; 36 | } // namespace model 37 | } // namespace state_chart 38 | 39 | namespace state_chart { 40 | 41 | class MockRuntime : public Runtime { 42 | public: 43 | MockRuntime() : is_running_(false) { 44 | using ::testing::Const; 45 | using ::testing::Return; 46 | 47 | ON_CALL(*this, mutable_datamodel()) 48 | .WillByDefault(Return(&default_datamodel_)); 49 | ON_CALL(*this, HasInternalEvent()).WillByDefault(Return(false)); 50 | ON_CALL(Const(*this), datamodel()) 51 | .WillByDefault(ReturnRef(default_datamodel_)); 52 | ON_CALL(*this, GetEventDispatcher()) 53 | .WillByDefault(Return(&default_event_dispatcher_)); 54 | } 55 | MockRuntime(const MockRuntime&) = delete; 56 | MockRuntime& operator=(const MockRuntime&) = delete; 57 | ~MockRuntime() override = default; 58 | 59 | MOCK_CONST_METHOD0(GetActiveStates, std::set()); 60 | MOCK_CONST_METHOD1(IsActiveState, bool(const string&)); 61 | MOCK_METHOD1(AddActiveState, void(const model::State* state)); 62 | MOCK_METHOD1(EraseActiveState, void(const model::State* state)); 63 | 64 | bool IsRunning() const override { return is_running_; } 65 | void SetRunning(bool is_running) override { is_running_ = is_running; } 66 | 67 | MOCK_CONST_METHOD0(HasInternalEvent, bool()); 68 | MOCK_METHOD0(DequeueInternalEvent, std::pair()); 69 | MOCK_METHOD2(EnqueueInternalEvent, 70 | void(const string& event, const string& payload)); 71 | 72 | MOCK_METHOD0(mutable_datamodel, Datamodel*()); 73 | MOCK_CONST_METHOD0(datamodel, const Datamodel&()); 74 | 75 | MockDatamodel& GetDefaultMockDatamodel() { return default_datamodel_; } 76 | 77 | MOCK_METHOD0(GetEventDispatcher, EventDispatcher*()); 78 | 79 | MockEventDispatcher& GetDefaultMockEventDispatcher() { 80 | return default_event_dispatcher_; 81 | } 82 | 83 | MOCK_METHOD0(Clear, void()); 84 | 85 | string DebugString() const override { return "MockRuntime"; } 86 | 87 | MOCK_CONST_METHOD0(Serialize, StateMachineContext::Runtime()); 88 | 89 | private: 90 | bool is_running_; 91 | 92 | testing::NiceMock default_datamodel_; 93 | testing::NiceMock default_event_dispatcher_; 94 | }; 95 | 96 | } // namespace state_chart 97 | 98 | #endif // STATE_CHART_INTERNAL_TESTING_MOCK_RUNTIME_H_ 99 | -------------------------------------------------------------------------------- /statechart/internal/testing/mock_state.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_TESTING_MOCK_STATE_H_ 18 | #define STATE_CHART_INTERNAL_TESTING_MOCK_STATE_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "statechart/platform/types.h" 25 | #include "statechart/internal/model/state.h" 26 | 27 | namespace state_chart { 28 | 29 | // Convenience typedef of gmock Return() actions for StateInterface. 30 | typedef std::set StateSet; 31 | typedef std::vector StateVec; 32 | 33 | class MockState : public model::State { 34 | public: 35 | explicit MockState(const string& id, bool is_final = false, 36 | bool is_parallel = false, 37 | const model::ExecutableContent* datamodel = nullptr, 38 | const model::ExecutableContent* on_entry = nullptr, 39 | const model::ExecutableContent* on_exit = nullptr) 40 | : model::State(id, is_final, is_parallel, datamodel, on_entry, on_exit) {} 41 | MockState(const MockState&) = delete; 42 | MockState& operator=(const MockState&) = delete; 43 | }; 44 | 45 | } // namespace state_chart 46 | 47 | #endif // STATE_CHART_INTERNAL_TESTING_MOCK_STATE_H_ 48 | -------------------------------------------------------------------------------- /statechart/internal/testing/mock_transition.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_INTERNAL_TESTING_MOCK_TRANSITION_H_ 18 | #define STATE_CHART_INTERNAL_TESTING_MOCK_TRANSITION_H_ 19 | 20 | #include "statechart/platform/types.h" 21 | #include "statechart/internal/model/transition.h" 22 | 23 | #include 24 | 25 | namespace state_chart { 26 | 27 | // Convenience typedef for gmock Return() actions: 28 | // EXPECT_THAT(model_, GetEventlessTransitions(_)) 29 | // .WillOnce(Return(Transitions{&transition1, &transition2})); 30 | typedef std::vector Transitions; 31 | 32 | class MockTransition : public model::Transition { 33 | public: 34 | MockTransition(const model::State* source, 35 | const std::vector& targets, 36 | const std::vector& events, 37 | const string& cond_expr = "", bool is_internal = false, 38 | const model::ExecutableContent* executable = nullptr) 39 | : Transition(source, targets, events, cond_expr, is_internal, 40 | executable) { 41 | using testing::_; 42 | using testing::Return; 43 | ON_CALL(*this, EvaluateCondition(_)).WillByDefault(Return(true)); 44 | } 45 | 46 | // Create a transition with a single target. 47 | MockTransition(const model::State* source, const model::State* target, 48 | const std::vector& events = {}, 49 | const string& cond_expr = "", 50 | const model::ExecutableContent* executable = nullptr) 51 | : MockTransition(source, target == nullptr 52 | ? std::vector{} 53 | : std::vector{target}, 54 | events, cond_expr, false, executable) {} 55 | 56 | MockTransition(const model::State* source, const model::State* target, 57 | const model::ExecutableContent* executable) 58 | : MockTransition(source, target, {} /* empty events */, "", executable) {} 59 | 60 | MockTransition(const MockTransition&) = delete; 61 | MockTransition& operator=(const MockTransition&) = delete; 62 | 63 | MOCK_CONST_METHOD1(EvaluateCondition, bool(Runtime*)); 64 | }; 65 | 66 | } // namespace state_chart 67 | 68 | #endif // STATE_CHART_INTERNAL_TESTING_MOCK_TRANSITION_H_ 69 | -------------------------------------------------------------------------------- /statechart/internal/utility.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/utility.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "absl/strings/escaping.h" 23 | #include "absl/strings/escaping.h" 24 | #include "absl/strings/str_cat.h" 25 | #include "absl/strings/str_join.h" 26 | #include "absl/strings/string_view.h" 27 | #include "absl/strings/substitute.h" 28 | #include "re2/re2.h" 29 | #include "statechart/platform/str_util.h" 30 | 31 | namespace state_chart { 32 | 33 | bool IsQuotedString(const string& str, const char quote_mark) { 34 | if (str.size() < 2 || str.front() != quote_mark || str.back() != quote_mark) { 35 | return false; 36 | } 37 | for (unsigned int i = 1; i < str.size() - 1; ++i) { 38 | if (str[i] == quote_mark && str[i - 1] != '\\') { 39 | return false; 40 | } 41 | } 42 | return true; 43 | } 44 | 45 | string Unquote(const string& str, const char quote_mark) { 46 | if (!IsQuotedString(str, quote_mark)) { 47 | return str; 48 | } 49 | // Unescape nested quotes. 50 | string result; 51 | strings::CharSet charset("\\"); 52 | charset.Add(quote_mark); 53 | strings::BackslashUnescape(absl::string_view(str.c_str() + 1, str.size() - 2), 54 | charset, &result); 55 | return result; 56 | } 57 | 58 | string Quote(const string& str) { 59 | if (IsQuotedString(str)) { 60 | return str; 61 | } 62 | // Escape nested quotes. 63 | return absl::StrCat("\"", EscapeQuotes(str), "\""); 64 | } 65 | 66 | string EscapeQuotes(const string& str) { 67 | string result; 68 | strings::BackslashEscape(str, strings::CharSet(R"(\")"), &result); 69 | return result; 70 | } 71 | 72 | bool MaybeJSON(const string& str) { 73 | // Ensure that JSON strings with newlines between braces will pass the match. 74 | static const LazyRE2 kJSONPattern = {R"RE((?s)\s*\{.*\}\s*)RE"}; 75 | return RE2::FullMatch(str, *kJSONPattern); 76 | } 77 | 78 | bool MaybeJSONArray(const string& str) { 79 | // Ensure that JSON arrays with newlines between braces will pass the match. 80 | static const LazyRE2 kJSONPattern = {R"RE((?s)\s*\[.*\]\s*)RE"}; 81 | return RE2::FullMatch(str, *kJSONPattern); 82 | } 83 | 84 | string MakeJSONError(const string& error_message) { 85 | return absl::StrCat("{\"error\": \"", EscapeQuotes(error_message).c_str(), 86 | "\"}"); 87 | } 88 | 89 | string MakeJSONFromStringMap(const std::map& data_map) { 90 | std::vector json_pair; 91 | for (const auto& data : data_map) { 92 | json_pair.push_back(absl::Substitute( 93 | "\"$0\":$1", EscapeQuotes(data.first).c_str(), data.second.c_str())); 94 | } 95 | return absl::StrCat("{", absl::StrJoin(json_pair, ","), "}"); 96 | } 97 | 98 | } // namespace state_chart 99 | -------------------------------------------------------------------------------- /statechart/internal/utility_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/internal/utility.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | namespace state_chart { 25 | namespace { 26 | 27 | TEST(UtilityTest, MakeJSONFromStringMapTest) { 28 | // Test empty case. 29 | EXPECT_EQ("{}", MakeJSONFromStringMap({})); 30 | // Test normal case with quote escaping. 31 | EXPECT_EQ( 32 | R"JSON({"key1":1,"key2":{},"key3":"unquoted","key4":"\"quoted\""})JSON", 33 | MakeJSONFromStringMap({{"key1", "1"}, 34 | {"key2", "{}"}, 35 | {"key3", R"("unquoted")"}, 36 | {"key4", R"("\"quoted\"")"}})); 37 | } 38 | 39 | TEST(UtilityTest, MaybeJSON) { 40 | // Ensure that JSON with newlines inside of them can be picked up as JSON. 41 | EXPECT_TRUE(MaybeJSON("{}")); 42 | EXPECT_TRUE(MaybeJSON("\n\n{\n\n\n}\n\n")); 43 | } 44 | 45 | TEST(UtilityTest, SearchWithPredicates) { 46 | std::vector binary{0, 1, 0, 0, 1, 1, 1, 0}; 47 | auto expected = binary.begin() + 2; 48 | auto actual = SearchWithPredicates( 49 | binary.begin(), binary.end(), 50 | std::vector>{[](int x) { return x == 0; }, 51 | [](int x) { return x == 0; }}); 52 | LOG(INFO) << "search expected: " << expected - binary.begin(); 53 | LOG(INFO) << "search actual: " << actual - binary.begin(); 54 | EXPECT_EQ(expected, actual); 55 | 56 | expected = binary.begin() + 5; 57 | actual = SearchWithPredicates( 58 | binary.begin(), binary.end(), 59 | std::vector>{ 60 | [](int x) { return x == 1; }, 61 | [](int x) { return x == 1; }, 62 | [](int x) { return x == 0; }, 63 | }); 64 | LOG(INFO) << "search expected: " << expected - binary.begin(); 65 | LOG(INFO) << "search actual: " << actual - binary.begin(); 66 | EXPECT_EQ(expected, actual); 67 | } 68 | 69 | TEST(UtilityTest, FindOuterMostParentheses) { 70 | struct TestCase { 71 | string sequence; 72 | int start; 73 | int end; 74 | }; 75 | 76 | TestCase cases[] = { 77 | // Fail cases. 78 | {"", 0, 0}, 79 | {"a", 1, 1}, 80 | {"(", 1, 1}, 81 | {"(()", 3, 3}, 82 | // Pass cases. 83 | {"(abc)", 0, 4}, 84 | {"(())", 0, 3}, 85 | {"()()", 0, 1}, 86 | {"(()())", 0, 5}, 87 | {"())", 0, 1}, 88 | }; 89 | 90 | for (const auto& test : cases) { 91 | auto result = FindOuterMostParentheses( 92 | test.sequence.begin(), test.sequence.end(), 93 | [](char x) { return x == '('; }, [](char x) { return x == ')'; }); 94 | LOG(INFO) << "FindParenthesis test: " << test.sequence; 95 | EXPECT_EQ(test.start, result.first - test.sequence.begin()); 96 | EXPECT_EQ(test.end, result.second - test.sequence.begin()); 97 | } 98 | } 99 | 100 | } // namespace 101 | } // namespace state_chart 102 | -------------------------------------------------------------------------------- /statechart/json_utils.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/json_utils.h" 16 | 17 | #include 18 | 19 | #include "absl/memory/memory.h" 20 | #include "absl/strings/str_cat.h" 21 | #include "absl/strings/substitute.h" 22 | #include "include/json/features.h" 23 | #include "include/json/reader.h" 24 | #include "include/json/value.h" 25 | #include "include/json/writer.h" 26 | #include "statechart/platform/protobuf.h" 27 | #include "statechart/platform/types.h" 28 | #include "statechart/platform/map_util.h" 29 | #include "statechart/proto/state_machine_context.pb.h" 30 | 31 | namespace state_chart { 32 | namespace json_utils { 33 | 34 | namespace { 35 | 36 | using gtl::LazyStaticPtr; 37 | using proto2::FieldDescriptor; 38 | using proto2::TextFormat; 39 | 40 | // A TextFormat::Printer which is initialized with correct FieldValuePrinters to 41 | // print JSON values. 42 | class JSONStyledPrinter : public TextFormat::Printer { 43 | public: 44 | class JSONFieldValuePrinter : public TextFormat::FieldValuePrinter { 45 | public: 46 | string PrintString(const string& json_string) const override; 47 | }; 48 | 49 | JSONStyledPrinter() : TextFormat::Printer() { 50 | SetExpandAny(true); 51 | const FieldDescriptor* datamodel_field_desc = 52 | ::state_chart::StateMachineContext::descriptor()->FindFieldByName( 53 | "datamodel"); 54 | 55 | auto json_field_value_printer = 56 | ::absl::make_unique(); 57 | if (RegisterFieldValuePrinter(datamodel_field_desc, 58 | json_field_value_printer.get())) { 59 | // If registered successfully, 'json_field_value_printer' will be owned 60 | // by the printer. 61 | json_field_value_printer.release(); 62 | } 63 | } 64 | }; 65 | 66 | // override 67 | string JSONStyledPrinter::JSONFieldValuePrinter::PrintString( 68 | const string& json_string) const { 69 | Json::Reader reader(Json::Features::strictMode()); 70 | Json::Value value; 71 | const bool success = 72 | reader.parse(json_string, value, false /* collectComments */); 73 | if (!success) { 74 | // probably is not even JSON. 75 | return TextFormat::FieldValuePrinter::PrintString(json_string); 76 | } 77 | Json::StyledWriter writer; 78 | string result = writer.write(value); 79 | absl::StripTrailingAsciiWhitespace(&result); 80 | return absl::StrCat("#-- JSON --# ", result, " #-- JSON --#"); 81 | } 82 | 83 | const TextFormat::Printer& GetJsonPrinter() { 84 | static LazyStaticPtr json_printer; 85 | return *json_printer; 86 | } 87 | 88 | } // namespace 89 | 90 | string DebugString(const proto2::Message& message) { 91 | string output; 92 | GetJsonPrinter().PrintToString(message, &output); 93 | return output; 94 | } 95 | 96 | } // namespace json_utils 97 | } // namespace state_chart 98 | -------------------------------------------------------------------------------- /statechart/json_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_JSON_UTILS_H_ 18 | #define STATE_CHART_JSON_UTILS_H_ 19 | 20 | #include 21 | 22 | #include "statechart/platform/types.h" 23 | #include "statechart/platform/protobuf.h" 24 | 25 | namespace state_chart { 26 | namespace json_utils { 27 | 28 | // Prints message.DebugString() with a custom printer that pretty prints 29 | // JSON datamodel inside StateMachineContext. 30 | string DebugString(const proto2::Message& message); 31 | 32 | } // namespace json_utils 33 | } // namespace state_chart 34 | 35 | #endif // STATE_CHART_JSON_UTILS_H_ 36 | -------------------------------------------------------------------------------- /statechart/json_utils_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/json_utils.h" 16 | 17 | #include 18 | 19 | #include "statechart/proto/state_machine_context.pb.h" 20 | 21 | 22 | namespace state_chart { 23 | namespace json_utils { 24 | namespace { 25 | 26 | const char kExpectedDebugString[] = R"(datamodel: #-- JSON --# { 27 | "baz" : { 28 | "nested" : "inside" 29 | }, 30 | "foo" : "bar", 31 | "hello" : "world" 32 | } #-- JSON --# 33 | )"; 34 | 35 | TEST(JsonUtils, DebugStringTest) { 36 | ::state_chart::StateMachineContext state_machine_context; 37 | state_machine_context.set_datamodel(R"( 38 | { "hello" : "world", "foo" : "bar", "baz" : { "nested" : "inside" } })"); 39 | EXPECT_EQ(kExpectedDebugString, 40 | json_utils::DebugString(state_machine_context)); 41 | } 42 | 43 | TEST(JsonUtils, DebugStringTestNotJson) { 44 | ::state_chart::StateMachineContext state_machine_context; 45 | state_machine_context.set_datamodel("Not \"really\" JSON"); 46 | EXPECT_EQ("datamodel: \"Not \\\"really\\\" JSON\"\n", 47 | json_utils::DebugString(state_machine_context)); 48 | } 49 | 50 | } // namespace 51 | } // namespace json_utils 52 | } // namespace state_chart 53 | -------------------------------------------------------------------------------- /statechart/logging.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // A set of macros that make the code more readable when using the early return 18 | // pattern on encountering an unexpected error. 19 | 20 | #ifndef STATE_CHART_LOGGING_H_ 21 | #define STATE_CHART_LOGGING_H_ 22 | 23 | #include 24 | #include 25 | 26 | #include "statechart/platform/logging.h" 27 | 28 | // TODO(srgandhe): Also log using DM_LOG once we can access DMLogSink here. 29 | // See b/27800056. 30 | #define RETURN_VALUE_IF_MSG(condition, value, msg) \ 31 | do { \ 32 | if (ABSL_PREDICT_FALSE((condition))) { \ 33 | LOG(DFATAL) << msg; \ 34 | return (value); \ 35 | } \ 36 | } while (false) 37 | 38 | #define RETURN_FALSE_IF_MSG(condition, msg) \ 39 | RETURN_VALUE_IF_MSG(condition, false, msg) 40 | 41 | #define RETURN_FALSE_IF(condition) \ 42 | RETURN_FALSE_IF_MSG(condition, "Returning false; condition (" \ 43 | << #condition << ") is true.") 44 | 45 | #define RETURN_NULL_IF_MSG(condition, msg) \ 46 | RETURN_VALUE_IF_MSG(condition, nullptr, msg) 47 | 48 | #define RETURN_NULL_IF(condition) \ 49 | RETURN_NULL_IF_MSG(condition, "Returning nullptr; condition (" \ 50 | << #condition << ") is true.") 51 | 52 | #define RETURN_IF_MSG(condition, msg) \ 53 | do { \ 54 | if (ABSL_PREDICT_FALSE((condition))) { \ 55 | LOG(DFATAL) << msg; \ 56 | return; \ 57 | } \ 58 | } while (false) 59 | 60 | #define RETURN_IF(condition) \ 61 | RETURN_IF_MSG(condition, "Returning; condition (" << #condition \ 62 | << ") is true.") 63 | 64 | #endif // STATE_CHART_LOGGING_H_ 65 | -------------------------------------------------------------------------------- /statechart/logging_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/logging.h" 16 | 17 | #include 18 | 19 | namespace state_chart { 20 | namespace { 21 | 22 | static const int kMagicNum = 42; 23 | 24 | int* ReturnIntPtr(int x) { 25 | RETURN_NULL_IF(x != kMagicNum); 26 | return new int(kMagicNum); 27 | } 28 | 29 | bool ReturnBool(int x) { 30 | RETURN_FALSE_IF(x != kMagicNum); 31 | return true; 32 | } 33 | 34 | TEST(ReturnNullIfTest, ReturnsNonNull) { 35 | std::unique_ptr ptr(ReturnIntPtr(kMagicNum)); 36 | EXPECT_NE(nullptr, ptr); 37 | } 38 | 39 | TEST(ReturnNullIfTest, ReturnsNull) { 40 | EXPECT_DEBUG_DEATH( 41 | { EXPECT_EQ(nullptr, ReturnIntPtr(20)); }, 42 | "Returning nullptr; condition \\(x != kMagicNum\\) is true."); 43 | } 44 | 45 | TEST(ReturnFalseIfTest, ReturnsTrue) { EXPECT_TRUE(ReturnBool(kMagicNum)); } 46 | 47 | TEST(ReturnFalseIfTest, ReturnsFalse) { 48 | EXPECT_DEBUG_DEATH( 49 | { EXPECT_FALSE(ReturnBool(20)); }, 50 | "Returning false; condition \\(x != kMagicNum\\) is true."); 51 | } 52 | 53 | } // namespace 54 | } // namespace state_chart 55 | -------------------------------------------------------------------------------- /statechart/platform/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The StateChart Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # A list of BUILD targets that are specific to platform being used. 16 | 17 | package(default_visibility = ["//visibility:public"]) 18 | 19 | cc_library( 20 | name = "types", 21 | hdrs = ["types.h"], 22 | ) 23 | 24 | cc_library( 25 | name = "logging", 26 | hdrs = ["logging.h"], 27 | ) 28 | 29 | cc_library( 30 | name = "map_util", 31 | hdrs = ["map_util.h"], 32 | deps = [ 33 | "@com_google_absl//absl/synchronization", 34 | "@com_google_glog//:glog", 35 | ], 36 | ) 37 | 38 | cc_library( 39 | name = "str_util", 40 | srcs = ["str_util.cc"], 41 | hdrs = ["str_util.h"], 42 | deps = [ 43 | ":types", 44 | "@com_google_absl//absl/strings", 45 | ], 46 | ) 47 | 48 | cc_library( 49 | name = "protobuf", 50 | hdrs = ["protobuf.h"], 51 | deps = [ 52 | ":types", 53 | "@com_google_glog//:glog", 54 | "@com_google_protobuf//:protobuf", 55 | ], 56 | ) 57 | 58 | cc_library( 59 | name = "test_util", 60 | testonly = 1, 61 | hdrs = ["test_util.h"], 62 | deps = [ 63 | ":protobuf", 64 | "@com_google_glog//:glog", 65 | "@com_google_googletest//:gtest", 66 | ], 67 | ) 68 | -------------------------------------------------------------------------------- /statechart/platform/logging.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATECHART_PLATFORM_LOGGING_H_ 18 | #define STATECHART_PLATFORM_LOGGING_H_ 19 | 20 | #define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0)) 21 | 22 | #endif // STATECHART_PLATFORM_LOGGING_H_ 23 | -------------------------------------------------------------------------------- /statechart/platform/protobuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATECHART_PLATFORM_PROTOBUF_H_ 18 | #define STATECHART_PLATFORM_PROTOBUF_H_ 19 | 20 | #include "google/protobuf/arena.h" 21 | #include "google/protobuf/compiler/importer.h" 22 | #include "google/protobuf/descriptor.h" 23 | #include "google/protobuf/io/coded_stream.h" 24 | #include "google/protobuf/io/zero_copy_stream.h" 25 | #include "google/protobuf/io/zero_copy_stream_impl_lite.h" 26 | #include "google/protobuf/map.h" 27 | #include "google/protobuf/repeated_field.h" 28 | #include "google/protobuf/text_format.h" 29 | #include "google/protobuf/util/json_util.h" 30 | #include "google/protobuf/util/type_resolver_util.h" 31 | #include "google/protobuf/util/message_differencer.h" 32 | #include "google/protobuf/stubs/status.h" 33 | 34 | #include "statechart/platform/types.h" 35 | 36 | #include 37 | 38 | using ::google::int64; 39 | 40 | namespace proto2 { 41 | using ::google::protobuf::RepeatedPtrField; 42 | using ::google::protobuf::RepeatedField; 43 | using ::google::protobuf::TextFormat; 44 | using ::google::protobuf::Message; 45 | using ::google::protobuf::MessageLite; 46 | using ::google::protobuf::Descriptor; 47 | using ::google::protobuf::DescriptorPool; 48 | using ::google::protobuf::FieldDescriptor; 49 | 50 | namespace util { 51 | 52 | using ::google::protobuf::util::MessageDifferencer; 53 | using ::google::protobuf::util::JsonStringToMessage; 54 | using ::google::protobuf::util::MessageToJsonString; 55 | 56 | class JsonFormat { 57 | public: 58 | 59 | enum Flags { 60 | ADD_WHITESPACE = 1 << 0, 61 | SYMBOLIC_ENUMS = 1 << 3, 62 | QUOTE_LARGE_INTS = 1 << 7, 63 | USE_JSON_OPT_PARAMETERS = 1 << 11, 64 | }; 65 | 66 | explicit JsonFormat(int64 flags) {}; 67 | virtual ~JsonFormat() = default; 68 | 69 | bool ParseFromString(const string& input, Message* output) const { 70 | google::protobuf::util::Status status = JsonStringToMessage(input, output); 71 | CHECK(status.ok()) << status.ToString(); 72 | return status.ok(); 73 | } 74 | 75 | bool PrintToString(const Message& message, string* output) const { 76 | google::protobuf::util::Status status = MessageToJsonString(message, output); 77 | CHECK(status.ok()) << status.ToString(); 78 | return status.ok(); 79 | } 80 | }; 81 | 82 | } // namespace util 83 | 84 | namespace contrib { 85 | namespace parse_proto { 86 | 87 | template 88 | T ParseTextOrDie(const string& input) { 89 | T result; 90 | CHECK(proto2::TextFormat::ParseFromString(input, &result)); 91 | return result; 92 | } 93 | 94 | } // namespace parse_proto 95 | } // namespace contrib 96 | 97 | 98 | } // namespace proto2 99 | 100 | #endif // STATECHART_PLATFORM_PROTOBUF_H_ 101 | -------------------------------------------------------------------------------- /statechart/platform/str_util.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/platform/str_util.h" 16 | 17 | namespace strings { 18 | 19 | CharSet::CharSet(absl::string_view characters) { 20 | for (const char& c : characters) { 21 | Add(c); 22 | } 23 | } 24 | 25 | void BackslashUnescape(absl::string_view src, 26 | const CharSet& to_unescape, string* dest) { 27 | typedef absl::string_view::const_iterator Iter; 28 | Iter first = src.begin(); 29 | Iter last = src.end(); 30 | bool escaped = false; 31 | for (; first != last; ++first) { 32 | if (escaped) { 33 | if (to_unescape.Test(*first)) { 34 | dest->push_back(*first); 35 | escaped = false; 36 | } else { 37 | dest->push_back('\\'); 38 | if (*first == '\\') { 39 | escaped = true; 40 | } else { 41 | escaped = false; 42 | dest->push_back(*first); 43 | } 44 | } 45 | } else { 46 | if (*first == '\\') { 47 | escaped = true; 48 | } else { 49 | dest->push_back(*first); 50 | } 51 | } 52 | } 53 | if (escaped) { 54 | dest->push_back('\\'); // trailing backslash 55 | } 56 | } 57 | 58 | void BackslashEscape(absl::string_view src, const CharSet& to_escape, 59 | string* dest) { 60 | typedef absl::string_view::const_iterator Iter; 61 | Iter first = src.begin(); 62 | Iter last = src.end(); 63 | while (first != last) { 64 | // Advance to next character we need to escape, or to end of source 65 | Iter next = first; 66 | while (next != last && !to_escape.Test(*next)) { 67 | ++next; 68 | } 69 | // Append the whole run of non-escaped chars 70 | dest->append(first, next); 71 | if (next != last) { 72 | // Char at *next needs to be escaped. 73 | char c[2] = {'\\', *next++}; 74 | dest->append(c, c + ABSL_ARRAYSIZE(c)); 75 | } 76 | first = next; 77 | } 78 | } 79 | 80 | } // namespace strings 81 | -------------------------------------------------------------------------------- /statechart/platform/str_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATECHART_PLATFORM_STR_UTIL_H_ 18 | #define STATECHART_PLATFORM_STR_UTIL_H_ 19 | 20 | #include 21 | 22 | #include "absl/strings/match.h" 23 | #include "absl/strings/string_view.h" 24 | #include "absl/strings/strip.h" 25 | #include "absl/strings/numbers.h" 26 | #include "statechart/platform/types.h" 27 | 28 | namespace strings { 29 | 30 | class CharSet { 31 | public: 32 | explicit CharSet(absl::string_view characters); 33 | 34 | // Add or remove a character from the set. 35 | void Add(unsigned char c) { bits_.set(c); } 36 | void Remove(unsigned char c) { bits_.reset(c); } 37 | 38 | // Return true if this character is in the set 39 | bool Test(unsigned char c) const { return bits_[c]; } 40 | 41 | private: 42 | std::bitset<256> bits_; 43 | }; 44 | 45 | void BackslashUnescape(absl::string_view src, const CharSet& to_unescape, 46 | string* dest); 47 | 48 | void BackslashEscape(absl::string_view src, const CharSet& to_escape, 49 | string* dest); 50 | 51 | inline string StripSuffixString(absl::string_view str, 52 | absl::string_view suffix) { 53 | return string(absl::StripSuffix(str, suffix)); 54 | } 55 | 56 | } // namespace strings 57 | 58 | #endif // STATECHART_PLATFORM_STR_UTIL_H_ 59 | -------------------------------------------------------------------------------- /statechart/platform/types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATECHART_PLATFORM_TYPES_H_ 18 | #define STATECHART_PLATFORM_TYPES_H_ 19 | 20 | #include 21 | 22 | using std::string; 23 | 24 | #endif // STATECHART_PLATFORM_TYPES_H_ 25 | -------------------------------------------------------------------------------- /statechart/proto/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The StateChart Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | package(default_visibility = ["//visibility:public"]) 16 | 17 | proto_library( 18 | name = "state_chart_proto", 19 | srcs = ["state_chart.proto"], 20 | ) 21 | 22 | proto_library( 23 | name = "state_machine_context_proto", 24 | srcs = ["state_machine_context.proto"], 25 | ) 26 | 27 | cc_proto_library( 28 | name = "state_chart_cc_proto", 29 | deps = [":state_chart_proto"], 30 | ) 31 | 32 | cc_proto_library( 33 | name = "state_machine_context_cc_proto", 34 | deps = [":state_machine_context_proto"], 35 | ) 36 | -------------------------------------------------------------------------------- /statechart/proto/state_machine_context.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto2"; 16 | 17 | option cc_enable_arenas = true; 18 | option java_outer_classname = "StateMachineContextProto"; 19 | option java_package = "com.google.statechart"; 20 | 21 | package state_chart; 22 | 23 | // A serialized representation of the internals of a StateMachine. 24 | message StateMachineContext { 25 | 26 | // A serialized representation of Runtime. 27 | message Runtime { 28 | 29 | // A serialized representation of active state elements in a state chart. 30 | // These state elements can be state, parallel or final. 31 | // These active state elements are arranged in a tree structure. 32 | // ActiveStateElement represents a node in such a tree. 33 | // TODO(srgandhe): Think of better format for serializing Runtime that 34 | // allows to store/restore arbitrary properties for states. 35 | message ActiveStateElement { 36 | // 'id' attribute of a StateElement. 37 | optional string id = 1; 38 | 39 | // A set of child element(s) that are active. 40 | repeated ActiveStateElement active_child = 2; 41 | } 42 | 43 | // A list of active states in this runtime. 44 | repeated ActiveStateElement active_state = 1; 45 | 46 | // Whether the state machine is running. 47 | optional bool running = 2; 48 | } 49 | 50 | optional Runtime runtime = 1; 51 | 52 | // Serialized string representation of the datamodel. This is obtained by 53 | // calling datamodel.SerializeAsString(). 54 | optional string datamodel = 2; 55 | } 56 | -------------------------------------------------------------------------------- /statechart/state_machine.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/state_machine.h" 16 | 17 | #include 18 | 19 | #include 20 | 21 | #include "statechart/platform/protobuf.h" 22 | #include "statechart/logging.h" 23 | 24 | using proto2::util::JsonFormat; 25 | 26 | namespace state_chart { 27 | 28 | StateMachine::StateMachine() 29 | : json_format_(JsonFormat::ADD_WHITESPACE | 30 | JsonFormat::QUOTE_LARGE_INTS | 31 | JsonFormat::USE_JSON_OPT_PARAMETERS | 32 | JsonFormat::SYMBOLIC_ENUMS) {} 33 | 34 | void StateMachine::SendEvent(const string& event, 35 | const proto2::Message* payload) { 36 | string json_payload; 37 | if (payload != nullptr) { 38 | mutable_json_format()->PrintToString(*payload, &json_payload); 39 | } 40 | SendEvent(event, json_payload); 41 | } 42 | 43 | proto2::util::JsonFormat* StateMachine::mutable_json_format() { 44 | return &json_format_; 45 | } 46 | 47 | const proto2::util::JsonFormat& StateMachine::json_format() const { 48 | return json_format_; 49 | } 50 | 51 | bool StateMachine::ExtractMessageFromDatamodel( 52 | const string& datamodel_location, proto2::Message* message_output) const { 53 | string json_object; 54 | const auto& datamodel = GetRuntime().datamodel(); 55 | if (!datamodel.IsDefined(datamodel_location) || 56 | !datamodel.EvaluateExpression(datamodel_location, &json_object)) { 57 | return false; 58 | } 59 | 60 | return json_format().ParseFromString(json_object, message_output); 61 | } 62 | 63 | bool StateMachine::SerializeToContext( 64 | StateMachineContext* state_machine_context) const { 65 | const auto& runtime = GetRuntime(); 66 | if (runtime.HasInternalEvent()) { 67 | return false; 68 | } 69 | *state_machine_context->mutable_runtime() = runtime.Serialize(); 70 | state_machine_context->set_datamodel(runtime.datamodel().SerializeAsString()); 71 | return true; 72 | } 73 | 74 | } // namespace state_chart 75 | -------------------------------------------------------------------------------- /statechart/state_machine_factory.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/state_machine_factory.h" 16 | 17 | #include 18 | 19 | #include "statechart/internal/datamodel.h" 20 | #include "statechart/internal/executor.h" 21 | #include "statechart/internal/function_dispatcher.h" 22 | #include "statechart/internal/light_weight_datamodel.h" 23 | #include "statechart/internal/model.h" 24 | #include "statechart/internal/model_builder.h" 25 | #include "statechart/internal/model_impl.h" 26 | #include "statechart/internal/runtime.h" 27 | #include "statechart/internal/runtime_impl.h" 28 | #include "statechart/internal/state_machine_impl.h" 29 | #include "statechart/internal/state_machine_logger.h" 30 | #include "statechart/logging.h" 31 | #include "statechart/platform/map_util.h" 32 | #include "statechart/proto/state_chart.pb.h" 33 | #include "statechart/proto/state_machine_context.pb.h" 34 | #include "statechart/state_machine.h" 35 | 36 | namespace state_chart { 37 | 38 | StateMachineFactory::StateMachineFactory() 39 | : StateMachineFactory(std::unique_ptr( 40 | ::absl::make_unique())) {} 41 | 42 | StateMachineFactory::StateMachineFactory( 43 | std::unique_ptr listener) 44 | : executor_(new Executor()), listener_(std::move(listener)) {} 45 | 46 | StateMachineFactory::~StateMachineFactory() {} 47 | 48 | bool StateMachineFactory::AddModelFromProto( 49 | const config::StateChart& state_chart) { 50 | RETURN_FALSE_IF(state_chart.name().empty()); 51 | const Model* model = ModelBuilder::CreateModelOrNull(state_chart); 52 | RETURN_FALSE_IF(model == nullptr); 53 | if (gtl::ContainsKey(models_, model->GetName())) { 54 | LOG(WARNING) << "Existing model replaced with:" << std::endl 55 | << state_chart.DebugString(); 56 | } 57 | models_[model->GetName()].reset(model); 58 | return true; 59 | } 60 | 61 | std::unique_ptr StateMachineFactory::CreateStateMachine( 62 | const string& model_name, FunctionDispatcher* function_dispatcher) const { 63 | const auto* model = gtl::FindOrNull(models_, model_name); 64 | RETURN_NULL_IF(model == nullptr || function_dispatcher == nullptr); 65 | // TODO(qplau): Create datamodel instance based on the model's datamodel type. 66 | std::unique_ptr state_machine = StateMachineImpl::Create( 67 | executor_.get(), model->get(), 68 | RuntimeImpl::Create(LightWeightDatamodel::Create(function_dispatcher))); 69 | RETURN_NULL_IF(state_machine == nullptr); 70 | state_machine->AddListener(listener_.get()); 71 | return state_machine; 72 | } 73 | 74 | std::unique_ptr StateMachineFactory::CreateStateMachine( 75 | const string& model_name, const StateMachineContext& state_machine_context, 76 | state_chart::FunctionDispatcher* function_dispatcher) const { 77 | const auto* model = gtl::FindOrNull(models_, model_name); 78 | RETURN_NULL_IF(model == nullptr); 79 | // TODO(qplau): Create datamodel instance based on the model's datamodel type. 80 | std::unique_ptr datamodel = LightWeightDatamodel::Create( 81 | state_machine_context.datamodel(), function_dispatcher); 82 | 83 | // Create Runtime. 84 | auto runtime = RuntimeImpl::Create(std::move(datamodel)); 85 | const auto& serialized_runtime = state_machine_context.runtime(); 86 | // Set the correct states as active in runtime. 87 | for (const auto* active_state : 88 | (*model)->GetActiveStates(serialized_runtime.active_state())) { 89 | runtime->AddActiveState(active_state); 90 | } 91 | runtime->SetRunning(serialized_runtime.running()); 92 | 93 | // Create StateMachine. 94 | std::unique_ptr state_machine = StateMachineImpl::Create( 95 | executor_.get(), model->get(), std::move(runtime)); 96 | RETURN_NULL_IF(state_machine == nullptr); 97 | state_machine->AddListener(listener_.get()); 98 | return state_machine; 99 | } 100 | 101 | bool StateMachineFactory::HasModel(const string& model_name) const { 102 | return gtl::ContainsKey(models_, model_name); 103 | } 104 | 105 | } // namespace state_chart 106 | -------------------------------------------------------------------------------- /statechart/state_machine_factory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_STATE_MACHINE_FACTORY_H_ 18 | #define STATE_CHART_STATE_MACHINE_FACTORY_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "absl/memory/memory.h" 27 | #include "statechart/internal/state_machine_logger.h" 28 | #include "statechart/proto/state_machine_context.pb.h" 29 | #include "statechart/state_machine_listener.h" 30 | 31 | namespace state_chart { 32 | class Executor; 33 | class FunctionDispatcher; 34 | class Model; 35 | class StateMachine; 36 | namespace config { 37 | class StateChart; 38 | } // namespace config 39 | } // namespace state_chart 40 | 41 | namespace state_chart { 42 | 43 | // Manages models and instantiates StateMachines based on these models. 44 | class StateMachineFactory { 45 | public: 46 | // Create a factory with models from a list of StateChart protos. 47 | // Template param 'Range' must be a range type like 'vector' or 'value_view' 48 | // of a map. 49 | // It also adds StateMachineListener 'listener' to all StateCharts created. 50 | // Returns nullptr if any of the StateCharts fails to be added to the factory. 51 | template 52 | static std::unique_ptr CreateFromProtos( 53 | const Range& state_charts, 54 | std::unique_ptr listener); 55 | 56 | // Same as above, with a StateMachineLogger as a default listener. 57 | template 58 | static std::unique_ptr CreateFromProtos( 59 | const Range& state_charts) { 60 | return CreateFromProtos(state_charts, 61 | std::unique_ptr( 62 | ::absl::make_unique())); 63 | } 64 | 65 | StateMachineFactory(const StateMachineFactory&) = delete; 66 | StateMachineFactory& operator=(const StateMachineFactory&) = delete; 67 | ~StateMachineFactory(); 68 | 69 | // Create a StateMachine instance given the model name and 70 | // 'function_dispatcher'. 71 | // Returns nullptr if !HasModel(model_name). 72 | // Note that the factory must outlive all StateMachines created from it. 73 | // Also function_dispatcher should outlive the StateMachine which uses it. 74 | std::unique_ptr CreateStateMachine( 75 | const string& model_name, 76 | state_chart::FunctionDispatcher* function_dispatcher) const; 77 | 78 | // Same as above but with an additional argument 'state_machine_context' 79 | // which stores serialized state of the state machine. 80 | std::unique_ptr CreateStateMachine( 81 | const string& model_name, 82 | const StateMachineContext& state_machine_context, 83 | state_chart::FunctionDispatcher* function_dispatcher) const; 84 | 85 | // Check that a given model exists, i.e., a state machine can be created from 86 | // this model name. 87 | bool HasModel(const string& model_name) const; 88 | 89 | protected: 90 | StateMachineFactory(); 91 | explicit StateMachineFactory(std::unique_ptr listener); 92 | 93 | // Adds a model from a StateChart proto. If a model with the same model name 94 | // exists, it will be replaced. 95 | // Returns false if there is a model creation error or if the state chart has 96 | // no name field. 97 | // Returns true if it successfully adds the StateChart Model to factory. 98 | bool AddModelFromProto(const config::StateChart& state_chart); 99 | 100 | private: 101 | std::unique_ptr executor_; 102 | std::unique_ptr listener_; 103 | std::map> models_; 104 | }; 105 | 106 | // static 107 | template 108 | std::unique_ptr StateMachineFactory::CreateFromProtos( 109 | const Range& state_charts, std::unique_ptr listener) { 110 | auto factory = 111 | ::absl::WrapUnique(new StateMachineFactory(std::move(listener))); 112 | for (const auto& state_chart : state_charts) { 113 | if (!factory->AddModelFromProto(state_chart)) { 114 | return nullptr; 115 | } 116 | } 117 | return factory; 118 | } 119 | 120 | } // namespace state_chart 121 | 122 | #endif // STATE_CHART_STATE_MACHINE_FACTORY_H_ 123 | -------------------------------------------------------------------------------- /statechart/state_machine_listener.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_STATE_MACHINE_LISTENER_H_ 18 | #define STATE_CHART_STATE_MACHINE_LISTENER_H_ 19 | 20 | #include 21 | 22 | #include "statechart/platform/types.h" 23 | 24 | namespace state_chart { 25 | 26 | class Runtime; 27 | 28 | namespace model { 29 | class State; 30 | class Transition; 31 | } // namespace model 32 | 33 | // A base class for clients that wish to listen on events and changes 34 | // of state within a running state machine. Treat this class as an interface. 35 | // Default NO-OP implementations are given out of convenience to the user. 36 | // 37 | // Sub-class this class and override methods of interest. Pass instances to 38 | // StateMachine::AddListener() to listen on a specific running state machine. 39 | class StateMachineListener { 40 | public: 41 | virtual ~StateMachineListener() {} 42 | 43 | // Invoked when a state is entered (becomes active), after the state's 44 | // "onentry" executable content has been executed. 45 | virtual void OnStateEntered(const Runtime* runtime, 46 | const model::State* state) {} 47 | 48 | // Invoked when a state is exited (becomes inactive), after the state's 49 | // "onexit" executable content has been executed. 50 | virtual void OnStateExited(const Runtime* runtime, 51 | const model::State* state) {} 52 | 53 | // Invoked when a transition is followed. Does not fire for "initial 54 | // transitions" (a transition that has no source state and whose target state 55 | // is the initial/start state of a StateMachine or of a compound or parallel 56 | // state within that StateMachine). 57 | // This is called after the source state has been exited (if applicable) 58 | // and the transition's executable content has been called, but before 59 | // the target state is entered (if applicable). 60 | virtual void OnTransitionFollowed(const Runtime* runtime, 61 | const model::Transition* transition) {} 62 | 63 | // Invoked every time a state machine executes a action. See: 64 | // http://www.w3.org/TR/scxml/#send 65 | // 66 | // The encoding of 'data' is specific to the specification of the 67 | // instance. If the element specifies a 'namelist' attribute, or 68 | // contains within it child elements, 'data' will be encoded as a JSON 69 | // dictionary corresponding to the values in the runtime's datamodel for each 70 | // location listed in 'namelist' or in the . If the element 71 | // specifies a child, the content is included as-is with no 72 | // modification. 73 | virtual void OnSendEvent(const Runtime* runtime, 74 | const string& event, 75 | const string& target, 76 | const string& type, 77 | const string& id, 78 | const string& data) {} 79 | }; 80 | 81 | } // namespace state_chart 82 | 83 | #endif // STATE_CHART_STATE_MACHINE_LISTENER_H_ 84 | -------------------------------------------------------------------------------- /statechart/state_machine_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "statechart/state_machine.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "statechart/internal/testing/mock_datamodel.h" 22 | #include "statechart/internal/testing/mock_runtime.h" 23 | #include "statechart/platform/test_util.h" 24 | #include "statechart/proto/state_machine_context.pb.h" 25 | #include "statechart/testing/mock_state_machine.h" 26 | #include "statechart/testing/state_machine_test.pb.h" 27 | 28 | 29 | using proto2::contrib::parse_proto::ParseTextOrDie; 30 | using testing::AtLeast; 31 | using testing::EqualsProtobuf; 32 | using testing::NotNull; 33 | using testing::Return; 34 | 35 | namespace state_chart { 36 | namespace { 37 | 38 | TEST(StateMachineTest, SendMessage_WithProto) { 39 | MockStateMachine sm; 40 | 41 | // Without payload. 42 | EXPECT_CALL(sm, SendEvent("no_payload", "")); 43 | sm.SendEvent("no_payload", nullptr); 44 | 45 | // With payload. 46 | StateMachineTestMessage message; 47 | message.set_value(10); 48 | 49 | EXPECT_CALL(sm, SendEvent("with_payload", "{\n \"value\": 10\n}\n")); 50 | sm.SendEvent("with_payload", &message); 51 | } 52 | 53 | TEST(StateMachineTest, ExtractMessageFromDatamodel) { 54 | MockStateMachine sm; 55 | 56 | MockDatamodel& datamodel = 57 | sm.GetDefaultMockRuntime().GetDefaultMockDatamodel(); 58 | 59 | // Test error cases first: location does not exists error, then JSON 60 | // conversion error. 61 | EXPECT_CALL(datamodel, IsDefined("error_location")).WillOnce(Return(false)); 62 | 63 | StateMachineTestMessage message; 64 | ASSERT_FALSE(sm.ExtractMessageFromDatamodel("error_location", &message)); 65 | 66 | // JSON conversion error. 67 | EXPECT_CALL(datamodel, IsDefined("malformed_location")) 68 | .WillOnce(Return(true)); 69 | EXPECT_CALL(datamodel, EvaluateExpression("malformed_location", NotNull())) 70 | .WillOnce(ReturnEvaluationResult("{\"value\": \"string_in_int_field\"}")); 71 | 72 | ASSERT_FALSE(sm.ExtractMessageFromDatamodel("malformed_location", &message)); 73 | 74 | // Success case. 75 | EXPECT_CALL(datamodel, IsDefined("json_location")).WillOnce(Return(true)); 76 | EXPECT_CALL(datamodel, EvaluateExpression("json_location", NotNull())) 77 | .WillOnce(ReturnEvaluationResult("{\"value\": 5}")); 78 | 79 | ASSERT_TRUE(sm.ExtractMessageFromDatamodel("json_location", &message)); 80 | 81 | EXPECT_EQ(5, message.value()); 82 | } 83 | 84 | TEST(StateMachineTest, ExtractMessageFromDatamodel_Extensions) { 85 | MockStateMachine sm; 86 | 87 | MockDatamodel& datamodel = 88 | sm.GetDefaultMockRuntime().GetDefaultMockDatamodel(); 89 | 90 | EXPECT_CALL(datamodel, IsDefined("json_location")).WillOnce(Return(true)); 91 | EXPECT_CALL(datamodel, EvaluateExpression("json_location", NotNull())) 92 | .WillOnce(ReturnEvaluationResult("{\"foo\": { \"ext_value\": 10 }}")); 93 | 94 | StateMachineTestMessage message; 95 | ASSERT_TRUE(sm.ExtractMessageFromDatamodel("json_location", &message)); 96 | 97 | EXPECT_TRUE(message.HasExtension(StateMachineTestExtension::ext)); 98 | EXPECT_EQ(10, message.GetExtension( 99 | StateMachineTestExtension::ext).ext_value()); 100 | } 101 | 102 | TEST(StateMachineTest, SerializeToContext) { 103 | MockStateMachine sm; 104 | 105 | MockRuntime& runtime = sm.GetDefaultMockRuntime(); 106 | MockDatamodel& datamodel = runtime.GetDefaultMockDatamodel(); 107 | 108 | EXPECT_CALL(sm, GetRuntime()).Times(AtLeast(1)); 109 | EXPECT_CALL(runtime, HasInternalEvent()).WillRepeatedly(Return(false)); 110 | 111 | const StateMachineContext kExpectedStateMachineContext = 112 | ParseTextOrDie(R"( 113 | runtime { 114 | active_state { 115 | id: "A" 116 | active_child { id : "A.a" } 117 | active_child { id : "A.b" } 118 | } 119 | active_state { id: "B" } 120 | running: true 121 | } 122 | datamodel: "something serialized" )"); 123 | 124 | EXPECT_CALL(runtime, Serialize()) 125 | .WillOnce(Return(kExpectedStateMachineContext.runtime())); 126 | EXPECT_CALL(datamodel, SerializeAsString()) 127 | .WillOnce(Return(kExpectedStateMachineContext.datamodel())); 128 | 129 | StateMachineContext state_machine_context; 130 | EXPECT_TRUE(sm.SerializeToContext(&state_machine_context)); 131 | EXPECT_THAT(state_machine_context, EqualsProtobuf(kExpectedStateMachineContext)); 132 | } 133 | 134 | } // namespace 135 | } // namespace state_chart 136 | -------------------------------------------------------------------------------- /statechart/testing/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The StateChart Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Description: 16 | # A package for testing utilities for StateChart library. 17 | 18 | package(default_visibility = ["//visibility:public"]) 19 | 20 | cc_library( 21 | name = "mock_state_machine", 22 | testonly = 1, 23 | hdrs = ["mock_state_machine.h"], 24 | deps = [ 25 | "//statechart:state_machine", 26 | "//statechart/internal/testing:mock_model", 27 | "//statechart/internal/testing:mock_runtime", 28 | "//statechart/platform:protobuf", 29 | ], 30 | ) 31 | 32 | proto_library( 33 | name = "state_machine_test_proto", 34 | srcs = ["state_machine_test.proto"], 35 | ) 36 | 37 | cc_proto_library( 38 | name = "state_machine_test_cc_proto", 39 | deps = [":state_machine_test_proto"], 40 | ) 41 | -------------------------------------------------------------------------------- /statechart/testing/mock_state_machine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The StateChart Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef STATE_CHART_TESTING_MOCK_STATE_MACHINE_H_ 18 | #define STATE_CHART_TESTING_MOCK_STATE_MACHINE_H_ 19 | 20 | #include "statechart/platform/protobuf.h" 21 | #include "statechart/internal/testing/mock_model.h" 22 | #include "statechart/internal/testing/mock_runtime.h" 23 | #include "statechart/state_machine.h" 24 | 25 | namespace state_chart { 26 | 27 | class MockStateMachine : public StateMachine { 28 | public: 29 | MockStateMachine() { 30 | // Delegate call to SendEvent() with proto payload to the parent class by 31 | // default. 32 | ON_CALL(*this, SendEvent(testing::_, testing::An())) 33 | .WillByDefault(testing::Invoke(this, &MockStateMachine::RealSendEvent)); 34 | 35 | ON_CALL(*this, GetRuntime()) 36 | .WillByDefault(testing::ReturnRef(default_runtime_)); 37 | ON_CALL(*this, GetModel()) 38 | .WillByDefault(testing::ReturnRef(default_model_)); 39 | } 40 | 41 | MockStateMachine(const MockStateMachine&) = delete; 42 | MockStateMachine& operator=(const MockStateMachine&) = delete; 43 | 44 | MOCK_METHOD0(Start, void()); 45 | 46 | MOCK_METHOD2(SendEvent, void(const string&, const string&)); 47 | MOCK_METHOD2(SendEvent, void(const string&, const proto2::Message*)); 48 | 49 | MOCK_CONST_METHOD0(GetRuntime, const Runtime&()); 50 | MOCK_CONST_METHOD0(GetModel, const Model&()); 51 | 52 | MOCK_METHOD1(AddListener, void(StateMachineListener* listener)); 53 | 54 | void RealSendEvent(const string& event, const proto2::Message* message) { 55 | StateMachine::SendEvent(event, message); 56 | } 57 | 58 | MockRuntime& GetDefaultMockRuntime() { return default_runtime_; } 59 | 60 | private: 61 | MockRuntime default_runtime_; 62 | MockModel default_model_; 63 | }; 64 | 65 | } // namespace state_chart 66 | 67 | #endif // STATE_CHART_TESTING_MOCK_STATE_MACHINE_H_ 68 | -------------------------------------------------------------------------------- /statechart/testing/state_machine_test.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The StateChart Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto2"; 16 | 17 | //import "net/proto2/util/proto/json_format.proto"; 18 | 19 | package state_chart; 20 | 21 | message StateMachineTestMessage { 22 | optional int32 value = 1; 23 | 24 | extensions 5 to 10; 25 | } 26 | 27 | message StateMachineTestExtension { 28 | optional int32 ext_value = 1; 29 | 30 | extend StateMachineTestMessage { 31 | optional StateMachineTestExtension ext = 5 [json_name = "foo"]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /third_party/BUILD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/statechart/d283cefab5a051a91c21be46d97bc18407fd7628/third_party/BUILD -------------------------------------------------------------------------------- /third_party/jsoncpp.BUILD: -------------------------------------------------------------------------------- 1 | licenses(["unencumbered"]) # Public Domain or MIT 2 | 3 | exports_files(["LICENSE"]) 4 | 5 | cc_library( 6 | name = "jsoncpp", 7 | srcs = [ 8 | "src/lib_json/json_reader.cpp", 9 | "src/lib_json/json_tool.h", 10 | "src/lib_json/json_value.cpp", 11 | "src/lib_json/json_writer.cpp", 12 | ], 13 | hdrs = [ 14 | "include/json/allocator.h", 15 | "include/json/assertions.h", 16 | "include/json/autolink.h", 17 | "include/json/config.h", 18 | "include/json/features.h", 19 | "include/json/forwards.h", 20 | "include/json/json.h", 21 | "include/json/reader.h", 22 | "include/json/value.h", 23 | "include/json/version.h", 24 | "include/json/writer.h", 25 | ], 26 | copts = ["-DJSON_USE_EXCEPTION=0"], 27 | includes = ["include"], 28 | visibility = ["//visibility:public"], 29 | deps = [":private"], 30 | ) 31 | 32 | cc_library( 33 | name = "private", 34 | textual_hdrs = [ 35 | "src/lib_json/json_valueiterator.inl", 36 | "src/lib_json/version.h.in", 37 | ], 38 | ) 39 | --------------------------------------------------------------------------------