├── .gitignore ├── CODE-OF-CONDUCT.md ├── Documentation └── FlowChart.odg ├── Examples ├── NStateManager.Example.Sale.Console │ ├── App.config │ ├── NStateManager.Example.Sale.Console.csproj │ ├── POSv1.dia │ ├── POSv1.png │ ├── POSv2.dia │ ├── POSv2.png │ ├── Payment.cs │ ├── Phone.dia │ ├── Phone.pdf │ ├── Phone.png │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Sale.cs │ ├── SaleItem.cs │ └── SaleStateMachine.cs └── NStateManager.Example.Sale │ ├── ChangeTransaction.cs │ ├── NStateManager.Example.Sale.csproj │ ├── Payment.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Sale.cs │ ├── SaleEvent.cs │ ├── SaleItem.cs │ ├── SaleItemState.cs │ ├── SaleState.cs │ ├── SaleStateManager.cs │ ├── Tests.cs │ └── packages.config ├── LICENSE ├── NSM Individual Contributor License Agreement.docx ├── README.md ├── Source ├── NStateManager.sln ├── NStateManager.sln.DotSettings ├── NStateManager.sln.licenseheader └── NStateManager │ ├── AssemblyInfo.cs │ ├── Async │ ├── IStateConfiguration.cs │ ├── IStateConfigurationInternal.cs │ ├── IStateMachine.cs │ ├── StateConfiguration.cs │ ├── StateMachine.cs │ ├── StateTransition.cs │ ├── StateTransitionAutoDynamic.cs │ ├── StateTransitionAutoDynamicParameterized.cs │ ├── StateTransitionAutoFallback.cs │ ├── StateTransitionAutoFallbackParameterized.cs │ ├── StateTransitionAutoForward.cs │ ├── StateTransitionAutoForwardParameterized.cs │ └── StateTransitionParameterized.cs │ ├── ExecutionParameters.cs │ ├── Export │ ├── ConfigurationSummary.cs │ ├── CsvExporter.cs │ ├── DotGraphExporter.cs │ ├── IStateMachine.cs │ ├── IStateMachineAsync.cs │ ├── StateDetails.cs │ ├── StateMachine.cs │ ├── StateMachineAsync.cs │ └── TransitionDetails.cs │ ├── Extensions.cs │ ├── FunctionAction.cs │ ├── FunctionActionBase.cs │ ├── FunctionActionParameterized.cs │ ├── NStateManager.csproj │ ├── NStateManager.nuspec │ ├── StateConfigurationBase.cs │ ├── StateTransitionBase.cs │ ├── StateTransitionDynamic.cs │ ├── StateTransitionDynamicAsync.cs │ ├── StateTransitionDynamicBase.cs │ ├── StateTransitionDynamicParameterized.cs │ ├── StateTransitionDynamicParameterizedAsync.cs │ ├── StateTransitionFactory.cs │ ├── StateTransitionNonDynamic.cs │ ├── StateTransitionResult.cs │ ├── Sync │ ├── IStateConfiguration.cs │ ├── IStateConfigurationInternal.cs │ ├── IStateMachine.cs │ ├── StateConfiguration.cs │ ├── StateMachine.cs │ ├── StateTransition.cs │ ├── StateTransitionAutoDynamic.cs │ ├── StateTransitionAutoDynamicParameterized.cs │ ├── StateTransitionAutoFallback.cs │ ├── StateTransitionAutoFallbackParameterized.cs │ ├── StateTransitionAutoForward.cs │ ├── StateTransitionAutoForwardParameterized.cs │ └── StateTransitionParameterized.cs │ ├── TransitionEventArgs.cs │ ├── TriggerAction.cs │ ├── TriggerActionBase.cs │ ├── TriggerActionFactory.cs │ └── TriggerActionParameterized.cs ├── Tests └── NStateManager.Tests │ ├── Async │ ├── StateConfigurationTests.cs │ ├── StateMachineTests.cs │ ├── StateTransitionAutoDynamicParameterizedTests.cs │ ├── StateTransitionAutoDynamicTests.cs │ ├── StateTransitionAutoFallbackParameterizedTests.cs │ ├── StateTransitionAutoFallbackTests.cs │ ├── StateTransitionAutoForwardParameterizedTests.cs │ ├── StateTransitionAutoForwardTests.cs │ ├── StateTransitionParameterizedTests.cs │ └── StateTransitionTests.cs │ ├── AutoFallbackTest.cs │ ├── ExecutionParametersTests.cs │ ├── Export │ ├── ConfigurationSummaryTests.cs │ ├── CsvExporterTests.cs │ ├── DotGraphExporterTests.cs │ ├── StateDetailsTests.cs │ ├── StateMachineAsyncTests.cs │ ├── StateMachineTests.cs │ └── StateTransitionDetailsTests.cs │ ├── FunctionActionParameterizedTests.cs │ ├── FunctionActionTests.cs │ ├── NStateManager.Tests.csproj │ ├── Phone.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Sale.cs │ ├── SaleComplex.cs │ ├── SaleComplexEvent.cs │ ├── SaleComplexState.cs │ ├── SimpleTests.cs │ ├── StateConfigurationBaseTests.cs │ ├── StateTransitionBaseTests.cs │ ├── StateTransitionDynamicAsyncTests.cs │ ├── StateTransitionDynamicParameterizedAsyncTests.cs │ ├── StateTransitionFactoryTests.cs │ ├── StateTransitionParameterizedTests.cs │ ├── StateTransitionTests.cs │ ├── Sync │ ├── PhoneCall.cs │ ├── StateConfigurationTests.cs │ ├── StateMachineTests.cs │ ├── StateTransitionAutoDynamicParameterizedTests.cs │ ├── StateTransitionAutoDynamicTests.cs │ ├── StateTransitionAutoFallbackParameterizedTests.cs │ ├── StateTransitionAutoFallbackTests.cs │ ├── StateTransitionAutoForwardParameterizedTests.cs │ ├── StateTransitionAutoForwardTests.cs │ ├── StateTransitionDynamicParameterizedTests.cs │ ├── StateTransitionDynamicTests.cs │ └── SubStateTest.cs │ ├── TestChange.cs │ ├── TestPayment.cs │ ├── TestProduct.cs │ ├── TestSale.cs │ ├── TestSaleEvent.cs │ ├── TestSaleItem.cs │ ├── TestSaleState.cs │ ├── TriggerActionParameterizedTests.cs │ └── packages.config └── appveyor.yml /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project email 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [scottctr@gmail.com][email]. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | [email]: mailto:scottctr@gmail.com 76 | -------------------------------------------------------------------------------- /Documentation/FlowChart.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Documentation/FlowChart.odg -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/App.config: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/NStateManager.Example.Sale.Console.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net472 4 | Exe 5 | true 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/POSv1.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/POSv1.dia -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/POSv1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/POSv1.png -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/POSv2.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/POSv2.dia -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/POSv2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/POSv2.png -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/Payment.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Example.Sale.Console 12 | { 13 | public class Payment 14 | { 15 | public double Amount { get; } 16 | 17 | public Payment(double amount) 18 | { 19 | Amount = amount; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/Phone.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/Phone.dia -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/Phone.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/Phone.pdf -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/Phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/Phone.png -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/Program.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Example.Sale.Console 12 | { 13 | internal class Program 14 | { 15 | private static void Main(string[] args) 16 | { 17 | var sale = new Sale(saleId: 1); 18 | SaleStateMachine.AddItem(sale, new SaleItem("Magazine", price: 3.00)); 19 | SaleStateMachine.AddItem(sale, new SaleItem("Fuel", price: 10.00)); 20 | SaleStateMachine.AddPayment(sale, new Payment(amount: 10.00)); 21 | SaleStateMachine.AddPayment(sale, new Payment(amount: 10.00)); 22 | SaleStateMachine.ReturnChange(sale, new Payment(amount: -7.00)); 23 | //We should be in Complete state here, so the following actions should be ignored 24 | SaleStateMachine.AddItem(sale, new SaleItem("Magazine", 3.00)); 25 | SaleStateMachine.AddItem(sale, new SaleItem("Fuel", 10.00)); 26 | 27 | System.Console.Read(); 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System.Reflection; 12 | using System.Runtime.InteropServices; 13 | 14 | // General Information about an assembly is controlled through the following 15 | // set of attributes. Change these attribute values to modify the information 16 | // associated with an assembly. 17 | [assembly: AssemblyTitle("NStateManager.Sale.Console")] 18 | [assembly: AssemblyDescription("")] 19 | [assembly: AssemblyConfiguration("")] 20 | [assembly: AssemblyCompany("")] 21 | [assembly: AssemblyProduct("NStateManager.Sale.Console")] 22 | [assembly: AssemblyCopyright("Copyright © 2018")] 23 | [assembly: AssemblyTrademark("")] 24 | [assembly: AssemblyCulture("")] 25 | 26 | // Setting ComVisible to false makes the types in this assembly not visible 27 | // to COM components. If you need to access a type in this assembly from 28 | // COM, set the ComVisible attribute to true on that type. 29 | [assembly: ComVisible(false)] 30 | 31 | // The following GUID is for the ID of the typelib if this project is exposed to COM 32 | [assembly: Guid("04c3f5c3-375f-4607-b9c5-44f4a7bf7833")] 33 | 34 | // Version information for an assembly consists of the following four values: 35 | // 36 | // Major Version 37 | // Minor Version 38 | // Build Number 39 | // Revision 40 | // 41 | // You can specify all the values or you can default the Build and Revision Numbers 42 | // by using the '*' as shown below: 43 | // [assembly: AssemblyVersion("1.0.*")] 44 | [assembly: AssemblyVersion("1.0.0.0")] 45 | [assembly: AssemblyFileVersion("1.0.0.0")] 46 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/Sale.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | 14 | namespace NStateManager.Example.Sale.Console 15 | { 16 | public enum SaleState 17 | { 18 | Open, 19 | ChangeDue, 20 | Complete 21 | } 22 | 23 | public enum SaleEvent 24 | { 25 | AddItem, 26 | Pay, 27 | ChangeGiven, 28 | SaleCancelled 29 | } 30 | 31 | public class Sale 32 | { 33 | public double Balance { get; private set; } 34 | public List Items { get; } = new List(); 35 | public List Payments { get; } = new List(); 36 | public SaleState State { get; set; } 37 | public int SaleId { get; } 38 | 39 | public Sale(int saleId) 40 | { 41 | SaleId = saleId; 42 | } 43 | 44 | public void AddItem(SaleItem item) 45 | { 46 | Items.Add(item); 47 | updateBalance(); 48 | } 49 | 50 | public void AddPayment(Payment payment) 51 | { 52 | Payments.Add(payment); 53 | updateBalance(); 54 | } 55 | 56 | public void ReturnChange() 57 | { 58 | AddPayment(new Payment(Balance)); 59 | } 60 | 61 | private void updateBalance() 62 | { 63 | Balance = Items.Sum(item => item.Price) - Payments.Sum(payment => payment.Amount); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/SaleItem.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Example.Sale.Console 12 | { 13 | public class SaleItem 14 | { 15 | public string Name { get; } 16 | public double Price { get; } 17 | 18 | public SaleItem(string name, double price) 19 | { 20 | Name = name; 21 | Price = price; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale.Console/SaleStateMachine.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using NStateManager.Sync; 12 | using System; 13 | using Output = System.Console; 14 | 15 | namespace NStateManager.Example.Sale.Console 16 | { 17 | public static class SaleStateMachine 18 | { 19 | private static readonly StateMachine StateMachine; 20 | 21 | static SaleStateMachine() 22 | { 23 | //State machine to manage sales for a point of sale system 24 | StateMachine = new StateMachine( 25 | stateAccessor: (sale) => sale.State 26 | ,stateMutator: (sale, state) => sale.State = state); 27 | 28 | //Log each time a sale changes state regardless of to/from state 29 | StateMachine.OnTransition += (sender, args) => Output.WriteLine($"Sale {args.Parameters.Context.SaleId} transitioned from {args.TransitionResult.PreviousState} to {args.TransitionResult.CurrentState}."); 30 | 31 | //Configure the Open state 32 | StateMachine.ConfigureState(SaleState.Open) 33 | //Process the new item on the AddItem event 34 | .AddTriggerAction(SaleEvent.AddItem, (sale, saleItem) => 35 | { 36 | sale.AddItem(saleItem); 37 | Output.WriteLine($"Adding {saleItem.Name} for {saleItem.Price:C}. {getSaleStatus(sale)}"); 38 | }) 39 | //Process the payment on the Pay event 40 | .AddTriggerAction(SaleEvent.Pay, (sale, payment) => 41 | { 42 | sale.AddPayment(payment); 43 | Output.WriteLine($"Adding payment of {payment.Amount:C}. {getSaleStatus(sale)}"); 44 | }) 45 | //Transition to the ChangeDue state if customer over pays 46 | .AddTransition(SaleEvent.Pay, SaleState.ChangeDue, condition: sale => sale.Balance < 0, name: "Open2ChangeDue", priority: 1) 47 | //Transition to the Completed state if customer pays exact amount 48 | .AddTransition(SaleEvent.Pay, SaleState.Complete, condition: sale => Math.Abs(sale.Balance) < .005, name: "Open2Complete", priority: 2); 49 | 50 | //Configure the ChangeDue state 51 | StateMachine.ConfigureState(SaleState.ChangeDue) 52 | //Process the returned change on the ChangeGiven event 53 | .AddTriggerAction(SaleEvent.ChangeGiven, (sale, payment) => 54 | { 55 | sale.ReturnChange(); 56 | Output.WriteLine($"Returning change of {payment.Amount:C}. {getSaleStatus(sale)}"); 57 | }) 58 | //Transition to the Complete state on ChangeGiven -- no conditions 59 | .AddTransition(SaleEvent.ChangeGiven, SaleState.Complete); 60 | 61 | //No configuration required for Complete state since it's a final state and 62 | //transitions or actions are taken at this point 63 | } 64 | 65 | private static string getSaleStatus(Sale sale) 66 | { 67 | return $"\r\n--> SaleState: {sale.State} \tBalance: {sale.Balance:$#,##0.00}."; 68 | } 69 | 70 | public static void AddItem(Sale sale, SaleItem saleItem) 71 | { 72 | StateMachine.FireTrigger(sale, SaleEvent.AddItem, saleItem); 73 | } 74 | 75 | public static void AddPayment(Sale sale, Payment payment) 76 | { 77 | StateMachine.FireTrigger(sale, SaleEvent.Pay, payment); 78 | } 79 | 80 | public static void ReturnChange(Sale sale, Payment payment) 81 | { 82 | StateMachine.FireTrigger(sale, SaleEvent.ChangeGiven, payment); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale/ChangeTransaction.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Example.Sale 12 | { 13 | public class ChangeTransaction 14 | { 15 | public decimal Amount { get; } 16 | 17 | public ChangeTransaction(decimal amount) 18 | { 19 | Amount = amount; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale/NStateManager.Example.Sale.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net5.0-windows 4 | Exe 5 | true 6 | true 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | all 28 | runtime; build; native; contentfiles; analyzers; buildtransitive 29 | 30 | 31 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale/Payment.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Example.Sale 12 | { 13 | public class Payment 14 | { 15 | public decimal Amount { get; } 16 | 17 | public Payment(decimal amount) 18 | { 19 | Amount = amount; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System.Reflection; 12 | using System.Runtime.InteropServices; 13 | 14 | // General Information about an assembly is controlled through the following 15 | // set of attributes. Change these attribute values to modify the information 16 | // associated with an assembly. 17 | [assembly: AssemblyTitle("NStateManager.Example.Sale")] 18 | [assembly: AssemblyDescription("")] 19 | [assembly: AssemblyConfiguration("")] 20 | [assembly: AssemblyCompany("")] 21 | [assembly: AssemblyProduct("NStateManager.Example.Sale")] 22 | [assembly: AssemblyCopyright("Copyright © 2018")] 23 | [assembly: AssemblyTrademark("")] 24 | [assembly: AssemblyCulture("")] 25 | 26 | // Setting ComVisible to false makes the types in this assembly not visible 27 | // to COM components. If you need to access a type in this assembly from 28 | // COM, set the ComVisible attribute to true on that type. 29 | [assembly: ComVisible(false)] 30 | 31 | // The following GUID is for the ID of the typelib if this project is exposed to COM 32 | [assembly: Guid("43520112-f273-4bd7-8bf8-5c5f1d474556")] 33 | 34 | // Version information for an assembly consists of the following four values: 35 | // 36 | // Major Version 37 | // Minor Version 38 | // Build Number 39 | // Revision 40 | // 41 | // You can specify all the values or you can default the Build and Revision Numbers 42 | // by using the '*' as shown below: 43 | // [assembly: AssemblyVersion("1.0.*")] 44 | [assembly: AssemblyVersion("1.0.0.0")] 45 | [assembly: AssemblyFileVersion("1.0.0.0")] 46 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale/SaleEvent.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Example.Sale 12 | { 13 | public enum SaleEvent 14 | { 15 | AddItem, 16 | AddPayment, 17 | ItemDelivered, 18 | ChangeGiven, 19 | Cancel 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale/SaleItem.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Example.Sale 12 | { 13 | public class SaleItem 14 | { 15 | public decimal Price { get; } 16 | 17 | public bool IsAuthorized => !IsCancelled && State >= SaleItemState.Authorized; 18 | 19 | public bool IsCancelled => State == SaleItemState.Cancelled; 20 | 21 | public bool IsDelivered => State == SaleItemState.Delivered; 22 | 23 | public bool IsRemovableAfterDelivery { get; internal set; } = true; 24 | 25 | public bool RequiresDelivery { get; internal set; } = false; 26 | 27 | public SaleItemState State { get; internal set; } 28 | 29 | public SaleItem(decimal price, SaleItemState state = SaleItemState.NotDelivered) 30 | { 31 | Price = price; 32 | State = state; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale/SaleItemState.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Example.Sale 12 | { 13 | public enum SaleItemState 14 | { 15 | NotDelivered, 16 | Authorized, 17 | Delivered, 18 | Cancelled 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale/SaleState.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Example.Sale 12 | { 13 | public enum SaleState 14 | { 15 | Opened, 16 | Authorized, 17 | WaitingForDelivery, 18 | ChangeDue, 19 | PartiallyAuthorized, 20 | Cancelled, 21 | Completed 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Examples/NStateManager.Example.Sale/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /NSM Individual Contributor License Agreement.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/NSM Individual Contributor License Agreement.docx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NStateManager [![Build status](https://ci.appveyor.com/api/projects/status/byg4n228cinno4xt?svg=true)](https://ci.appveyor.com/project/ScottCarter/nstatemanager) [![NuGet Status](https://img.shields.io/nuget/v/NStateManager.svg)](https://www.nuget.org/packages/NStateManager) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=NStateManager&metric=security_rating)](https://sonarcloud.io/dashboard?id=NStateManager) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=NStateManager&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=NStateManager) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=NStateManager&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=NStateManager) 2 | # Background and Goals 3 | **NStateManager** is a .Net library for managing: 4 | - [definition of states](https://github.com/scottctr/NStateManager/wiki/Quick-Start#states) for a type 5 | - [rules for transitioning](https://github.com/scottctr/NStateManager/wiki/Changing-States) between states 6 | - additional [actions to take](https://github.com/scottctr/NStateManager/wiki/Event-Actions) based on trigger events and current state 7 | 8 | This project was created while developing cloud-based solutions. We needed a state management solution made for our stateless services. 9 | - **Stateless** so it's thread safe and a single state machine can be used to manage state for all managed instances 10 | - **Intuitive configuration** of [state change conditions](https://github.com/scottctr/NStateManager/wiki/Changing-States), [behaviors based on current state](https://github.com/scottctr/NStateManager/wiki/Event-Actions), and [actions when changing states](https://github.com/scottctr/NStateManager/wiki/State-Change-Actions) 11 | - **[async/await support](https://github.com/scottctr/NStateManager/wiki/Async-Await-Support)**, including cancellation and ConfigureAwait 12 | # Quick Example 13 | Managing state of a Sale for a simple point-of-sale system. 14 | 15 | ![POSv1](https://github.com/scottctr/NStateManager/blob/master/Examples/NStateManager.Example.Sale.Console/POSv2.png) 16 | ### Configuring the state machine 17 | ```C# 18 | //State machine to manage sales for a point of sale system 19 | stateMachine = new StateMachine( 20 | stateAccessor: (sale) => sale.State, //stateAccessor is used to retrieve the current state 21 | stateMutator: (sale, state) => sale.State = state); //stateMutator updates state based on transition rule below 22 | 23 | //Log each time a sale changes state regardless of to/from state 24 | stateMachine.RegisterOnTransitionedAction((sale, transitionDetails) 25 | => Output.WriteLine($"Sale {sale.SaleID} transitioned from {transitionDetails.PreviousState} to {transitionDetails.CurrentState}.")); 26 | 27 | //Configure the Open state 28 | stateMachine.ConfigureState(SaleState.Open) 29 | //Process the new item on the AddItem event 30 | .AddTriggerAction(SaleEvent.AddItem, (sale, saleItem) => { sale.AddItem(saleItem); }) 31 | //Process the payment on the Pay event 32 | .AddTriggerAction(SaleEvent.Pay, (sale, payment) => { sale.AddPayment(payment); }) 33 | //Transition to the ChangeDue state on Pay event if customer over pays 34 | .AddTransition(SaleEvent.Pay, SaleState.ChangeDue, condition: sale => sale.Balance < 0, name: "Open2ChangeDue", priority: 1) 35 | //Transition to the Completed state on Pay event if customer pays exact amount 36 | .AddTransition(SaleEvent.Pay, SaleState.Complete, condition: sale => sale.Balance == 0, name: "Open2Complete", priority: 2); 37 | 38 | //Configure the ChangeDue state 39 | stateMachine.ConfigureState(SaleState.ChangeDue) 40 | //Process the returned change on the ChangeGiven event 41 | .AddTriggerAction(SaleEvent.ChangeGiven, (sale, payment) => { sale.ReturnChange(); }) 42 | //Transition to the Complete state on ChangeGiven -- no conditions 43 | .AddTransition(SaleEvent.ChangeGiven, SaleState.Complete); 44 | 45 | //No configuration required for Complete state since it's a final state and 46 | //no state transitions or actions are allowed at this point 47 | ``` 48 | ### Using the state machine 49 | ```C# 50 | //Add an item to a sale 51 | stateMachine.FireTrigger(sale, SaleEvent.AddItem, saleItem); 52 | 53 | //Add a payment to a sale 54 | stateMachine.FireTrigger(sale, SaleEvent.Pay, payment); 55 | 56 | //Give the customer their change 57 | stateMachine.FireTrigger(sale, SaleEvent.ChangeGiven, payment); 58 | ``` 59 | For a walkthough of the above code, go to the [Quick Start](https://github.com/scottctr/NStateManager/wiki/Quick-Start). You can also look at the [Wiki](https://github.com/scottctr/NStateManager/wiki) for additional details. 60 | # Feedback 61 | Feedback, questions, advice, and contributions are always welcomed so feel free to leave your thoughts in [Issues](https://github.com/scottctr/NStateManager/issues). 62 | -------------------------------------------------------------------------------- /Source/NStateManager.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30717.126 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NStateManager", "NStateManager\NStateManager.csproj", "{32ABF9D2-580A-4D63-BC38-8EAD7E8B405A}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NStateManager.Example.Sale.Console", "..\Examples\NStateManager.Example.Sale.Console\NStateManager.Example.Sale.Console.csproj", "{04C3F5C3-375F-4607-B9C5-44F4A7BF7833}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NStateManager.Example.Sale", "..\Examples\NStateManager.Example.Sale\NStateManager.Example.Sale.csproj", "{43520112-F273-4BD7-8BF8-5C5F1D474556}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NStateManager.Tests", "..\Tests\NStateManager.Tests\NStateManager.Tests.csproj", "{2ADDF230-A48B-462B-A1D4-CD91D9EC5F6F}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {32ABF9D2-580A-4D63-BC38-8EAD7E8B405A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {32ABF9D2-580A-4D63-BC38-8EAD7E8B405A}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {32ABF9D2-580A-4D63-BC38-8EAD7E8B405A}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {32ABF9D2-580A-4D63-BC38-8EAD7E8B405A}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {04C3F5C3-375F-4607-B9C5-44F4A7BF7833}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {04C3F5C3-375F-4607-B9C5-44F4A7BF7833}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {04C3F5C3-375F-4607-B9C5-44F4A7BF7833}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {04C3F5C3-375F-4607-B9C5-44F4A7BF7833}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {43520112-F273-4BD7-8BF8-5C5F1D474556}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {43520112-F273-4BD7-8BF8-5C5F1D474556}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {43520112-F273-4BD7-8BF8-5C5F1D474556}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {43520112-F273-4BD7-8BF8-5C5F1D474556}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {2ADDF230-A48B-462B-A1D4-CD91D9EC5F6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {2ADDF230-A48B-462B-A1D4-CD91D9EC5F6F}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {2ADDF230-A48B-462B-A1D4-CD91D9EC5F6F}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {2ADDF230-A48B-462B-A1D4-CD91D9EC5F6F}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {BDB508A4-9C98-4F2D-9E3A-A0270F42F3DB} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /Source/NStateManager.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | <data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="NStateManager.Example.Sale.Console" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /><Filter ModuleMask="*.Tests" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data> 3 | <data /> -------------------------------------------------------------------------------- /Source/NStateManager.sln.licenseheader: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Source/NStateManager.sln.licenseheader -------------------------------------------------------------------------------- /Source/NStateManager/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System.Reflection; 12 | using System.Runtime.CompilerServices; 13 | using System.Runtime.InteropServices; 14 | 15 | // General Information about an assembly is controlled through the following 16 | // set of attributes. Change these attribute values to modify the information 17 | // associated with an assembly. 18 | [assembly: AssemblyTitle("NStateManager")] 19 | [assembly: AssemblyDescription("")] 20 | [assembly: AssemblyConfiguration("")] 21 | [assembly: AssemblyCompany("Scott L Carter")] 22 | [assembly: AssemblyProduct("NStateManager")] 23 | [assembly: AssemblyCopyright("Copyright © 2019")] 24 | [assembly: AssemblyTrademark("")] 25 | [assembly: AssemblyCulture("")] 26 | [assembly: InternalsVisibleTo("NStateManager.Tests")] 27 | 28 | // Setting ComVisible to false makes the types in this assembly not visible 29 | // to COM components. If you need to access a type in this assembly from 30 | // COM, set the ComVisible attribute to true on that type. 31 | [assembly: ComVisible(false)] 32 | 33 | // The following GUID is for the ID of the typelib if this project is exposed to COM 34 | [assembly: Guid("38251772-c63b-4815-af8f-1920ac0f12bb")] 35 | 36 | // Version information for an assembly consists of the following four values: 37 | // 38 | // Major Version 39 | // Minor Version 40 | // Build Number 41 | // Revision 42 | // 43 | [assembly: AssemblyVersion("4.0.1")] 44 | [assembly: AssemblyFileVersion("4.0.1")] 45 | -------------------------------------------------------------------------------- /Source/NStateManager/Async/IStateConfigurationInternal.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System.Threading.Tasks; 12 | 13 | namespace NStateManager.Async 14 | { 15 | public interface IStateConfigurationInternal : IStateConfiguration 16 | { 17 | void AddAutoTransition(TTrigger trigger, StateTransitionBase transition); 18 | void AddSuperstate(IStateConfigurationInternal superStateConfiguration); 19 | void AddTransition(TTrigger trigger, StateTransitionBase transition); 20 | Task> ExecuteAutoTransitionAsync(ExecutionParameters parameters 21 | , StateTransitionResult currentResult); 22 | Task ExecuteEntryActionAsync(ExecutionParameters parameters, StateTransitionResult currentResult); 23 | Task ExecuteExitActionAsync(ExecutionParameters parameters, StateTransitionResult currentResult); 24 | Task ExecuteReentryActionAsync(ExecutionParameters parameters, StateTransitionResult currentResult); 25 | Task> FireTriggerAsync(ExecutionParameters parameters); 26 | 27 | bool IsSubState(); 28 | 29 | /// 30 | /// Returns true if the given state is the same as the current state or the current state is a subset of the given state; otherwise false. 31 | /// 32 | /// The state to compare against. 33 | /// 34 | bool IsSubStateOf(TState state); 35 | IStateConfigurationInternal SuperStateConfig { get; } 36 | TState State { get; } 37 | } 38 | } -------------------------------------------------------------------------------- /Source/NStateManager/Async/IStateMachine.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager.Async 16 | { 17 | public partial interface IStateMachine 18 | where TState : IComparable 19 | { 20 | /// 21 | /// Defines an action to take any time occurs. 22 | /// 23 | /// The for the action. 24 | /// The action to execute. 25 | /// also has trigger actions that should only occur while T is in a specific state. 26 | /// 27 | IStateMachine AddTriggerAction(TTrigger trigger, Func action); 28 | 29 | /// 30 | /// Defines an action to take any time occurs. 31 | /// 32 | /// Parameter to be passed in from FireTrigger. 33 | /// The for the action. 34 | /// The action to execute. 35 | /// also has trigger actions that should only occur while T is in a specific state. 36 | /// 37 | IStateMachine AddTriggerAction(TTrigger trigger, Func action); 38 | 39 | /// 40 | /// Configures a specified . 41 | /// 42 | /// The to configure. 43 | /// 44 | IStateConfiguration ConfigureState(TState state); 45 | 46 | /// 47 | /// Executes trigger asynchronously with a parameter. 48 | /// 49 | /// The items whose state is being managed. 50 | /// The event that has occurred that may affect the state. 51 | /// The details of the event that's occurring. 52 | /// Provides the ability to cancel an asynchronous operation. 53 | /// 54 | Task> FireTriggerAsync(T context, TTrigger trigger, TRequest request, CancellationToken cancellationToken = default) 55 | where TRequest : class; 56 | 57 | /// 58 | /// Executes trigger. 59 | /// 60 | /// The items whose state is being managed. 61 | /// The event that has occurred that may affect the state. 62 | /// Provides the ability to cancel an asynchronous operation. 63 | /// 64 | Task> FireTriggerAsync(T context, TTrigger trigger, CancellationToken cancellationToken = default); 65 | 66 | bool IsInState(T context, TState state); 67 | 68 | Func StateAccessor { get; } 69 | Action StateMutator { get; } 70 | 71 | /// 72 | /// Event raised when the context transitions to a new state when FireTrigger is called. 73 | /// 74 | event EventHandler> OnTransition; 75 | 76 | /// 77 | /// Event raised when the context doesn't transition to a new state when FireTrigger is called. 78 | /// 79 | event EventHandler> OnNoTransition; 80 | 81 | /// 82 | /// Event raised when the context's current state isn't configured for the trigger passed to FireTrigger. 83 | /// 84 | event EventHandler> OnTriggerNotConfigured; 85 | } 86 | } -------------------------------------------------------------------------------- /Source/NStateManager/Async/StateTransition.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager.Async 16 | { 17 | internal class StateTransition : StateTransitionNonDynamic 18 | { 19 | public Func> ConditionAsync { get; } 20 | public bool HasCondition { get; } 21 | 22 | public StateTransition(Func stateAccessor, Action stateMutator, TState toState, Func> conditionAsync, string name, uint priority) 23 | : base(stateAccessor, stateMutator, toState, name, priority) 24 | { 25 | if (conditionAsync == null) 26 | { return; } 27 | 28 | ConditionAsync = conditionAsync; 29 | HasCondition = true; 30 | } 31 | 32 | public override async Task> ExecuteAsync(ExecutionParameters parameters 33 | , StateTransitionResult currentResult = null) 34 | { 35 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context); 36 | 37 | if (parameters.CancellationToken.IsCancellationRequested) 38 | { return GetFreshResult(parameters, currentResult, startState, wasCancelled: true, transitionDefined: true, conditionMet: false); } 39 | 40 | if (HasCondition && !await ConditionAsync(parameters.Context, parameters.CancellationToken) 41 | .ConfigureAwait(continueOnCapturedContext: false)) 42 | { return GetFreshResult(parameters, currentResult, startState, wasCancelled: parameters.CancellationToken.IsCancellationRequested, transitionDefined: true, conditionMet: false); } 43 | 44 | StateMutator(parameters.Context, ToState); 45 | var transitionResult = GetFreshResult(parameters, currentResult, startState, wasCancelled: false, conditionMet: true, transitionDefined: true); 46 | 47 | return transitionResult; 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Source/NStateManager/Async/StateTransitionAutoDynamic.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading.Tasks; 13 | 14 | namespace NStateManager.Async 15 | { 16 | internal class StateTransitionAutoDynamic : StateTransitionDynamicBase 17 | where TState : IComparable 18 | { 19 | private readonly IStateMachine _stateMachine; 20 | private readonly TState _startState; 21 | private readonly TState _triggerState; 22 | private readonly Func _stateFunction; 23 | 24 | public StateTransitionAutoDynamic(IStateMachine stateMachine 25 | , TState startStartState 26 | , Func targetStateFunction 27 | , TState triggerState 28 | , string name 29 | , uint priority) 30 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, name, priority) 31 | { 32 | _stateMachine = stateMachine; 33 | _startState = startStartState; 34 | _triggerState = triggerState; 35 | _stateFunction = targetStateFunction; 36 | } 37 | 38 | public override Task> ExecuteAsync(ExecutionParameters parameters 39 | , StateTransitionResult currentResult = null) 40 | { 41 | if (currentResult != null 42 | && !parameters.CancellationToken.IsCancellationRequested 43 | && _startState.IsEqual(currentResult.PreviousState) 44 | && (_triggerState.IsEqual(currentResult.CurrentState) 45 | || _stateMachine.IsInState(parameters.Context, _triggerState))) 46 | { 47 | StateMutator(parameters.Context, _stateFunction.Invoke(parameters.Context)); 48 | 49 | var transitioned = !StateAccessor(parameters.Context).IsEqual(_triggerState); 50 | var result = GetFreshResult(parameters 51 | , currentResult 52 | , currentResult.StartingState 53 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested 54 | , transitionDefined: true 55 | , conditionMet: transitioned); 56 | 57 | return Task.FromResult(result); 58 | } 59 | 60 | return Task.FromResult(GetFreshResult(parameters 61 | , currentResult 62 | , StateAccessor(parameters.Context) 63 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested 64 | , transitionDefined: true 65 | , conditionMet: false)); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Source/NStateManager/Async/StateTransitionAutoDynamicParameterized.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager.Async 14 | { 15 | internal class StateTransitionAutoDynamicParameterized : StateTransitionDynamicBase 16 | where TState : IComparable 17 | where TRequest : class 18 | { 19 | private readonly IStateMachine _stateMachine; 20 | private readonly TState _startState; 21 | private readonly TState _triggerState; 22 | private readonly Func _stateFunction; 23 | 24 | public StateTransitionAutoDynamicParameterized(IStateMachine stateMachine 25 | , TState startState 26 | , Func targetStateFunction 27 | , TState triggerState 28 | , string name 29 | , uint priority) 30 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, name, priority) 31 | { 32 | _stateMachine = stateMachine; 33 | _startState = startState; 34 | _triggerState = triggerState; 35 | _stateFunction = targetStateFunction; 36 | } 37 | 38 | public override StateTransitionResult Execute(ExecutionParameters parameters 39 | , StateTransitionResult currentResult = null) 40 | { 41 | if (!(parameters.Request is TRequest typeSafeParam)) 42 | { throw new ArgumentException($"Expected a {typeof(TRequest).Name} parameter, but received a {parameters.Request?.GetType().Name ?? "null"}."); } 43 | 44 | if (currentResult != null 45 | && !parameters.CancellationToken.IsCancellationRequested 46 | && _startState.IsEqual(currentResult.PreviousState) 47 | && (_triggerState.IsEqual(currentResult.CurrentState) 48 | || _stateMachine.IsInState(parameters.Context, _triggerState))) 49 | { 50 | StateMutator(parameters.Context, _stateFunction(parameters.Context, typeSafeParam)); 51 | 52 | var transitioned = !StateAccessor(parameters.Context).IsEqual(_triggerState); 53 | var result = GetFreshResult(parameters 54 | , currentResult 55 | , currentResult.StartingState 56 | , wasCancelled: false 57 | , transitionDefined: true 58 | , conditionMet: transitioned); 59 | 60 | return result; 61 | } 62 | 63 | return GetFreshResult(parameters 64 | , currentResult 65 | , StateAccessor(parameters.Context) 66 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested 67 | , transitionDefined: true 68 | , conditionMet: false); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Source/NStateManager/Async/StateTransitionAutoFallback.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager.Async 16 | { 17 | internal class StateTransitionAutoFallback : StateTransition 18 | where TState : IComparable 19 | { 20 | private readonly IStateMachine _stateMachine; 21 | private readonly TState _startState; 22 | private readonly TState _triggerState; 23 | 24 | public StateTransitionAutoFallback(IStateMachine stateMachine, TState startState, TState triggerState, TState toState, Func> conditionAsync, string name, uint priority) 25 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, conditionAsync, name, priority) 26 | { 27 | _stateMachine = stateMachine; 28 | _startState = startState; 29 | _triggerState = triggerState; 30 | } 31 | 32 | public override async Task> ExecuteAsync(ExecutionParameters parameters 33 | , StateTransitionResult currentResult = null) 34 | { 35 | if (currentResult != null 36 | && !parameters.CancellationToken.IsCancellationRequested 37 | && _startState.IsEqual(currentResult.PreviousState) 38 | && (_triggerState.IsEqual(currentResult.CurrentState) 39 | || _stateMachine.IsInState(parameters.Context, _triggerState))) 40 | { return await base.ExecuteAsync(parameters, currentResult); } 41 | 42 | return GetFreshResult(parameters 43 | , currentResult 44 | , StateAccessor(parameters.Context) 45 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested 46 | , transitionDefined: true 47 | , conditionMet: false); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Source/NStateManager/Async/StateTransitionAutoFallbackParameterized.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager.Async 16 | { 17 | internal class StateTransitionAutoFallbackParameterized : StateTransitionParameterized 18 | where TParam : class 19 | where TState : IComparable 20 | { 21 | private readonly IStateMachine _stateMachine; 22 | private readonly TState _startState; 23 | private readonly TState _triggerState; 24 | 25 | public StateTransitionAutoFallbackParameterized(IStateMachine stateMachine, TState startState, TState toState, TState triggerState, Func> conditionAsync, string name, uint priority) 26 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, conditionAsync, name, priority) 27 | { 28 | _stateMachine = stateMachine; 29 | _startState = startState; 30 | _triggerState = triggerState; 31 | } 32 | 33 | public override async Task> ExecuteAsync(ExecutionParameters parameters 34 | , StateTransitionResult currentResult = null) 35 | { 36 | if (currentResult != null 37 | && !parameters.CancellationToken.IsCancellationRequested 38 | && _startState.IsEqual(currentResult.PreviousState) 39 | && (_triggerState.IsEqual(currentResult.CurrentState) 40 | || _stateMachine.IsInState(parameters.Context, _triggerState))) 41 | { return await base.ExecuteAsync(parameters, currentResult); } 42 | 43 | return GetFreshResult(parameters 44 | , currentResult 45 | , StateAccessor(parameters.Context) 46 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested 47 | , transitionDefined: true 48 | , conditionMet: false); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Source/NStateManager/Async/StateTransitionAutoForward.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager.Async 16 | { 17 | internal class StateTransitionAutoForward : StateTransition 18 | where TState : IComparable 19 | { 20 | private readonly IStateMachine _stateMachine; 21 | private readonly TState _triggerState; 22 | 23 | public StateTransitionAutoForward(IStateMachine stateMachine, TState triggerState, TState toState, Func> conditionAsync, string name, uint priority) 24 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, conditionAsync, name, priority) 25 | { 26 | _stateMachine = stateMachine; 27 | _triggerState = triggerState; 28 | } 29 | 30 | public override async Task> ExecuteAsync(ExecutionParameters parameters 31 | , StateTransitionResult currentResult = null) 32 | { 33 | if (currentResult != null 34 | && !parameters.CancellationToken.IsCancellationRequested 35 | && (_triggerState.IsEqual(currentResult.CurrentState) 36 | || _stateMachine.IsInState(parameters.Context, _triggerState))) 37 | { return await base.ExecuteAsync(parameters, currentResult); } 38 | 39 | return GetFreshResult(parameters 40 | , currentResult 41 | , StateAccessor(parameters.Context) 42 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested 43 | , transitionDefined: true 44 | , conditionMet: false); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /Source/NStateManager/Async/StateTransitionAutoForwardParameterized.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager.Async 16 | { 17 | internal class StateTransitionAutoForwardParameterized : StateTransitionParameterized 18 | where TParam : class 19 | where TState : IComparable 20 | { 21 | private readonly IStateMachine _stateMachine; 22 | private readonly TState _triggerState; 23 | 24 | public StateTransitionAutoForwardParameterized(IStateMachine stateMachine, TState toState, TState triggerState, Func> conditionAsync, string name, uint priority) 25 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, conditionAsync, name, priority) 26 | { 27 | _stateMachine = stateMachine; 28 | _triggerState = triggerState; 29 | } 30 | 31 | public override async Task> ExecuteAsync(ExecutionParameters parameters 32 | , StateTransitionResult currentResult = null) 33 | { 34 | if (currentResult != null //Auto transitions must have a previously successful transition 35 | && !parameters.CancellationToken.IsCancellationRequested 36 | && (_triggerState.IsEqual(currentResult.CurrentState) 37 | || _stateMachine.IsInState(parameters.Context, _triggerState))) 38 | { return await base.ExecuteAsync(parameters, currentResult); } 39 | 40 | return GetFreshResult(parameters 41 | , currentResult 42 | , StateAccessor(parameters.Context) 43 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested 44 | , transitionDefined: true 45 | , conditionMet: false); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Source/NStateManager/Async/StateTransitionParameterized.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager.Async 16 | { 17 | internal class StateTransitionParameterized : StateTransitionNonDynamic 18 | where TParam : class 19 | { 20 | public Func> ConditionAsync { get; } 21 | 22 | public StateTransitionParameterized(Func stateAccessor, Action stateMutator, TState toState, Func> conditionAsync, string name, uint priority) 23 | : base(stateAccessor, stateMutator, toState, name, priority) 24 | { 25 | ConditionAsync = conditionAsync ?? throw new ArgumentNullException(nameof(conditionAsync)); 26 | } 27 | 28 | public override async Task> ExecuteAsync(ExecutionParameters parameters 29 | , StateTransitionResult currentResult = null) 30 | { 31 | if (!(parameters.Request is TParam typeSafeParam)) 32 | { throw new ArgumentException($"Expected a {typeof(TParam).Name} parameter, but received a {parameters.Request?.GetType().Name ?? "null"}."); } 33 | 34 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context); 35 | 36 | if (parameters.CancellationToken.IsCancellationRequested) 37 | { 38 | return GetFreshResult(parameters 39 | , currentResult 40 | , startState 41 | , transitionDefined: true 42 | , conditionMet: false 43 | , wasCancelled: true); } 44 | 45 | if (!await ConditionAsync(parameters.Context, typeSafeParam, parameters.CancellationToken) 46 | .ConfigureAwait(continueOnCapturedContext: false)) 47 | { return GetFreshResult(parameters, currentResult, startState, transitionDefined: true, conditionMet: false, wasCancelled: parameters.CancellationToken.IsCancellationRequested); } 48 | 49 | StateMutator(parameters.Context, ToState); 50 | var transitionResult = currentResult == null 51 | ? new StateTransitionResult(parameters.Trigger, startState, startState, ToState, Name) 52 | : new StateTransitionResult(parameters.Trigger, startState, currentResult.CurrentState, ToState, Name); 53 | 54 | return transitionResult; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Source/NStateManager/ExecutionParameters.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System.Threading; 12 | 13 | namespace NStateManager 14 | { 15 | public class ExecutionParameters 16 | { 17 | public TTrigger Trigger { get; } 18 | public T Context { get; } 19 | public object Request { get; } 20 | public CancellationToken CancellationToken { get; } 21 | 22 | public ExecutionParameters(TTrigger trigger 23 | , T context 24 | , CancellationToken cancellationToken = default 25 | , object request = null) 26 | { 27 | Trigger = trigger; 28 | Context = context; 29 | Request = request; 30 | CancellationToken = cancellationToken; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/NStateManager/Export/ConfigurationSummary.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Collections.Generic; 13 | using System.Linq; 14 | 15 | namespace NStateManager.Export 16 | { 17 | public class ConfigurationSummary where TState : IComparable 18 | { 19 | private readonly Dictionary> _stateDetails = new Dictionary>(); 20 | public IReadOnlyList> StateDetails => _stateDetails.Values.ToList(); 21 | 22 | public IReadOnlyList> FinalStates => Transitions 23 | .Where(t => t.ToState.IsFinalState) 24 | .Select(t => t.ToState) 25 | .Distinct() 26 | .ToList(); 27 | 28 | public bool HasSubStates => StateDetails.Any(s => s.IsSuperState); 29 | 30 | public IReadOnlyList> StartingStates => Transitions 31 | .Where(t => t.FromState.IsStartingState) 32 | .Select(t => t.FromState) 33 | .Distinct() 34 | .ToList(); 35 | 36 | public IReadOnlyList> Transitions => _stateDetails.SelectMany(s => s.Value.TransitionsFrom).Distinct().ToList(); 37 | 38 | public StateDetails AddState(TState state) 39 | { 40 | if (!_stateDetails.ContainsKey(state)) 41 | { _stateDetails.Add(state, new StateDetails(state)); } 42 | 43 | return _stateDetails[state]; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Source/NStateManager/Export/CsvExporter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace NStateManager.Export 6 | { 7 | public class CsvExporter where TState : IComparable 8 | { 9 | public static string Export(ConfigurationSummary summary) 10 | { 11 | var csv = new StringBuilder(); 12 | csv.AppendLine("FromState, TriggerEvent, ToState, Condition"); 13 | 14 | foreach (var transition in summary.Transitions.OrderByDescending(t => t.FromState.IsStartingState).ThenByDescending(t => t.ToState.IsFinalState)) 15 | { csv.AppendLine($"{transition.FromState.State}, {transition.Trigger}, {transition.ToState.State}{(transition.HasCondition ? ", *" : string.Empty)}"); } 16 | 17 | var unusedStates = summary.StateDetails.Where(s => s.IsStartingState && s.IsFinalState); 18 | foreach (var state in unusedStates) 19 | { csv.AppendLine(state.State.ToString()); } 20 | 21 | return csv.ToString(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Source/NStateManager/Export/DotGraphExporter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace NStateManager.Export 7 | { 8 | public class DotGraphExporter where TState : IComparable 9 | { 10 | public static string Export(ConfigurationSummary summary) 11 | { 12 | var dotGraph = new StringBuilder(); 13 | dotGraph.AppendLine("digraph NStateManagerGraph {"); 14 | if (summary.HasSubStates) 15 | { dotGraph.AppendLine("\tcompound=true;"); } 16 | 17 | // ReSharper disable once StringLiteralTypo 18 | dotGraph.AppendLine("\trankdir=LR;"); 19 | dotGraph.AppendLine(); 20 | 21 | exportNonSuperStates(summary.StateDetails.Where(s => s.StateLevel == 0) , dotGraph, level: 0); 22 | exportSuperStates(summary.StateDetails.Where(s => s.StateLevel == 1), dotGraph, level: 0); 23 | 24 | //List all the transitions. 25 | if (summary.Transitions.Any()) 26 | { 27 | dotGraph.AppendLine(); 28 | 29 | foreach (var transition in summary.Transitions) 30 | { 31 | dotGraph.Append($"\t{transition.FromState.State} -> {transition.ToState.State}"); 32 | dotGraph.Append(" [label=\""); 33 | 34 | if (!string.IsNullOrWhiteSpace(transition.Name)) 35 | { dotGraph.Append(transition.Trigger); } 36 | 37 | if (transition.HasCondition) 38 | { dotGraph.Append("(*)"); } 39 | dotGraph.Append("\""); 40 | 41 | if (transition.FromState.IsSuperState) 42 | { dotGraph.Append($" ltail=cluster_{transition.FromState.State}"); } 43 | 44 | if (transition.ToState.IsSuperState) 45 | { dotGraph.Append($" lhead=cluster_{transition.ToState.State}"); } 46 | 47 | dotGraph.AppendLine("];"); 48 | } 49 | } 50 | 51 | dotGraph.AppendLine("}"); 52 | 53 | return dotGraph.ToString(); 54 | } 55 | 56 | private static void exportSuperStates(IEnumerable> stateDetails, StringBuilder dotGraph, int level) 57 | { 58 | foreach (var state in stateDetails) 59 | { 60 | dotGraph.AppendLine(); 61 | dotGraph.AppendLine($"{createIndent(level + 1)}subgraph cluster_{state.State} {{"); 62 | dotGraph.AppendLine($"{createIndent(level + 2)}label={state.State};"); 63 | exportSubGraphNode(dotGraph, state); 64 | exportNonSuperStates(state.SubStates.Where(s => s.IsSuperState == false), dotGraph, level: level + 1); 65 | exportSuperStates(state.SubStates.Where(s => s.IsSuperState), dotGraph, level + 1); 66 | dotGraph.AppendLine($"{createIndent(level + 1)}}}"); 67 | } 68 | } 69 | 70 | private static void exportSubGraphNode(StringBuilder dotGraph, StateDetails state) 71 | { 72 | dotGraph.Append($"{createIndent(state.StateLevel + 1)}{state.State} "); 73 | dotGraph.AppendLine(state.TransitionsFrom.Any(t => t.ToState?.SuperState == state) 74 | ? "[shape=circle label=\"\" style=filled color=black]" 75 | : "[shape=point color=white];"); 76 | } 77 | 78 | private static string createIndent(int level) 79 | { 80 | var indent = string.Empty; 81 | for(var i = 0; i < level; i++) 82 | { indent += "\t"; } 83 | 84 | return indent; 85 | } 86 | 87 | private static void exportNonSuperStates(IEnumerable> stateDetails, StringBuilder dotGraph, int level) 88 | { 89 | var stateDetailsArray = stateDetails as StateDetails[] ?? stateDetails.ToArray(); 90 | 91 | if (!stateDetailsArray.Any()) 92 | { return; } 93 | 94 | dotGraph.AppendLine(); 95 | 96 | foreach (var state in stateDetailsArray) 97 | { dotGraph.AppendLine($"{createIndent(level + 1)}{state.State};"); } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /Source/NStateManager/Export/IStateMachine.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using NStateManager.Export; 12 | using System; 13 | 14 | namespace NStateManager.Sync 15 | { 16 | public partial interface IStateMachine 17 | where TState : IComparable 18 | { 19 | ConfigurationSummary GetSummary(); 20 | } 21 | } -------------------------------------------------------------------------------- /Source/NStateManager/Export/IStateMachineAsync.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using NStateManager.Export; 12 | using System; 13 | 14 | // ReSharper disable once CheckNamespace 15 | namespace NStateManager.Async 16 | { 17 | public partial interface IStateMachine 18 | where TState : IComparable 19 | { 20 | ConfigurationSummary GetSummary(); 21 | } 22 | } -------------------------------------------------------------------------------- /Source/NStateManager/Export/TransitionDetails.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager.Export 14 | { 15 | public class TransitionDetails where TState : IComparable 16 | { 17 | public TransitionDetails(string name 18 | , StateDetails fromState 19 | , TTrigger trigger 20 | , StateDetails toState = null 21 | , bool hasCondition = false 22 | , uint priority = 1 23 | , bool isDynamic = false) 24 | { 25 | Name = name; 26 | FromState = fromState; 27 | Trigger = trigger; 28 | ToState = toState; 29 | HasCondition = hasCondition; 30 | Priority = priority; 31 | IsDynamic = isDynamic; 32 | } 33 | 34 | public string Name { get; } 35 | public StateDetails FromState { get; } 36 | public TTrigger Trigger { get; } 37 | public StateDetails ToState { get; } 38 | public bool HasCondition { get; } 39 | public uint Priority { get; } 40 | public bool IsDynamic { get; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Source/NStateManager/Extensions.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager 14 | { 15 | public static class Extensions 16 | { 17 | public static bool IsEqual(this TState first, TState second) where TState : IComparable 18 | { 19 | return first.CompareTo(second) == 0; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/NStateManager/FunctionAction.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager 16 | { 17 | internal class FunctionAction : FunctionActionBase 18 | { 19 | internal Func Action { get; } 20 | 21 | internal FunctionAction(Func action) 22 | { 23 | Action = action ?? throw new ArgumentNullException(nameof(action)); 24 | } 25 | 26 | internal override async Task ExecuteAsync(T context, CancellationToken cancellationToken, object request) 27 | { 28 | await Action.Invoke(context, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Source/NStateManager/FunctionActionBase.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | 14 | namespace NStateManager 15 | { 16 | public abstract class FunctionActionBase 17 | { 18 | internal abstract Task ExecuteAsync(T context, CancellationToken cancellationToken, object request); 19 | } 20 | } -------------------------------------------------------------------------------- /Source/NStateManager/FunctionActionParameterized.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager 16 | { 17 | internal class FunctionActionParameterized : FunctionActionBase 18 | { 19 | internal Func Action { get; } 20 | 21 | internal FunctionActionParameterized(Func action) 22 | { 23 | Action = action ?? throw new ArgumentNullException(nameof(action)); 24 | } 25 | 26 | internal override async Task ExecuteAsync(T context, CancellationToken cancellationToken, object request) 27 | { 28 | if (!(request is TRequest typedRequestType)) 29 | { throw new ArgumentException($"{nameof(request)} must be of type {typeof(TRequest).Name}.");} 30 | 31 | await Action.Invoke(context, typedRequestType, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Source/NStateManager/NStateManager.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 5.0 6 | false 7 | true 8 | true 9 | 4.0.2 10 | Scott L Carter 11 | Scott L Carter 12 | Copyright (c) 2019 Scott L. Carter 13 | https://github.com/scottctr/NStateManager/blob/master/LICENSE 14 | https://github.com/scottctr/NStateManager 15 | https://github.com/scottctr/NStateManager 16 | NStateManager 17 | 4.0.2 18 | 4.0.2 19 | 20 | Easy to use and very flexible state manager for .Net. 21 | latest 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Source/NStateManager/NStateManager.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $id$ 5 | $version$ 6 | $title$ 7 | $author$ 8 | $author$ 9 | http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE 10 | http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE 11 | http://ICON_URL_HERE_OR_DELETE_THIS_LINE 12 | false 13 | $description$ 14 | Summary of changes made in this release of the package. 15 | Copyright 2018 16 | Tag1 Tag2 17 | 18 | -------------------------------------------------------------------------------- /Source/NStateManager/StateTransitionBase.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading.Tasks; 13 | 14 | namespace NStateManager 15 | { 16 | public abstract class StateTransitionBase 17 | { 18 | protected internal string Name; 19 | protected internal uint Priority; 20 | protected Func StateAccessor { get; } 21 | protected Action StateMutator { get; } 22 | 23 | protected StateTransitionBase(Func stateAccessor 24 | , Action stateMutator 25 | , string name 26 | , uint priority) 27 | { 28 | StateAccessor = stateAccessor ?? throw new ArgumentNullException(nameof(stateAccessor)); 29 | StateMutator = stateMutator ?? throw new ArgumentNullException(nameof(stateMutator)); 30 | Name = name ?? ""; 31 | Priority = priority; 32 | } 33 | 34 | public virtual StateTransitionResult Execute(ExecutionParameters parameters 35 | , StateTransitionResult currentResult = null) 36 | { 37 | throw new NotImplementedException("Inherited classes must override this method. Ensure you're calling the correct overloaded version."); 38 | } 39 | 40 | public virtual Task> ExecuteAsync(ExecutionParameters parameters 41 | , StateTransitionResult currentResult = null) 42 | { 43 | throw new NotImplementedException("Inherited classes must override this method. Ensure you're calling the correct overloaded version."); 44 | } 45 | 46 | protected StateTransitionResult GetFreshResult(ExecutionParameters parameters 47 | , StateTransitionResult previousResult 48 | , TState startState 49 | , bool transitionDefined 50 | , bool conditionMet 51 | , bool wasCancelled) 52 | { 53 | var wasSuccessful = (transitionDefined && conditionMet && !wasCancelled); 54 | 55 | return new StateTransitionResult(parameters.Trigger 56 | , startState 57 | , (!wasSuccessful || previousResult == null) ? startState : previousResult.CurrentState 58 | , wasSuccessful ? StateAccessor(parameters.Context) : previousResult == null ? startState : previousResult.CurrentState 59 | , wasSuccessful ? Name : string.Empty 60 | , wasCancelled: wasCancelled 61 | , conditionMet: conditionMet 62 | , transitionDefined: transitionDefined); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Source/NStateManager/StateTransitionDynamic.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager 14 | { 15 | internal class StateTransitionDynamic : StateTransitionDynamicBase 16 | where TState : IComparable 17 | { 18 | public Func StateFunc { get; } 19 | 20 | public StateTransitionDynamic(Func stateAccessor 21 | , Action stateMutator 22 | , Func stateFunc 23 | , string name 24 | , uint priority) 25 | : base(stateAccessor, stateMutator, name, priority) 26 | { 27 | StateFunc = stateFunc ?? throw new ArgumentNullException(nameof(stateFunc)); 28 | } 29 | 30 | public override StateTransitionResult Execute(ExecutionParameters parameters, StateTransitionResult currentResult = null) 31 | { 32 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context); 33 | var toState = StateFunc(parameters.Context); 34 | 35 | StateMutator(parameters.Context, toState); 36 | 37 | var transitioned = !StateAccessor(parameters.Context).IsEqual(startState); 38 | var transitionResult = GetFreshResult(parameters, currentResult, startState, wasCancelled: false, transitionDefined: true, conditionMet: transitioned); 39 | 40 | return transitionResult; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Source/NStateManager/StateTransitionDynamicAsync.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager 16 | { 17 | internal class StateTransitionDynamicAsync : StateTransitionDynamicBase 18 | where TState : IComparable 19 | { 20 | public Func> StateFunctionAsync { get; } 21 | 22 | public StateTransitionDynamicAsync(Func stateAccessor 23 | , Action stateMutator 24 | , Func> stateFuncAsync 25 | , string name 26 | , uint priority) 27 | : base(stateAccessor, stateMutator, name, priority) 28 | { 29 | StateFunctionAsync = stateFuncAsync ?? throw new ArgumentNullException(nameof(stateFuncAsync)); 30 | } 31 | 32 | public override async Task> ExecuteAsync(ExecutionParameters parameters 33 | , StateTransitionResult currentResult = null) 34 | { 35 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context); 36 | 37 | if (parameters.CancellationToken.IsCancellationRequested) 38 | { return GetFreshResult(parameters, currentResult, startState, wasCancelled: true, transitionDefined: true, conditionMet: false); } 39 | 40 | var toState = await StateFunctionAsync(parameters.Context, parameters.CancellationToken) 41 | .ConfigureAwait(continueOnCapturedContext: false); 42 | 43 | return ExecuteFinalizeAsync(parameters, currentResult, startState, toState); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Source/NStateManager/StateTransitionDynamicBase.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager 14 | { 15 | internal abstract class StateTransitionDynamicBase : StateTransitionBase 16 | where TState : IComparable 17 | { 18 | protected StateTransitionDynamicBase(Func stateAccessor, Action stateMutator, string name, uint priority) 19 | : base(stateAccessor, stateMutator, name, priority) 20 | { } 21 | } 22 | } -------------------------------------------------------------------------------- /Source/NStateManager/StateTransitionDynamicParameterized.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager 14 | { 15 | internal class StateTransitionDynamicParameterized : StateTransitionDynamicBase 16 | where TParam : class 17 | where TState : IComparable 18 | { 19 | public Func StateFunc { get; } 20 | 21 | public StateTransitionDynamicParameterized(Func stateAccessor, Action stateMutator, Func stateFunc, string name, uint priority) 22 | : base(stateAccessor, stateMutator, name, priority) 23 | { 24 | StateFunc = stateFunc ?? throw new ArgumentNullException(nameof(stateFunc)); 25 | } 26 | 27 | public override StateTransitionResult Execute(ExecutionParameters parameters, StateTransitionResult currentResult = null) 28 | { 29 | if (!(parameters.Request is TParam typeSafeParam)) 30 | { throw new ArgumentException($"Expected a {typeof(TParam).Name} parameter, but received a {parameters.Request?.GetType().Name ?? "null"}."); } 31 | 32 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context); 33 | var toState = StateFunc(parameters.Context, typeSafeParam); 34 | 35 | var transitioned = !toState.IsEqual(startState); 36 | 37 | if (transitioned) 38 | { StateMutator(parameters.Context, toState); } 39 | 40 | var transitionResult = GetFreshResult(parameters, currentResult, startState, transitionDefined: true, wasCancelled: false, conditionMet: transitioned); 41 | 42 | return transitionResult; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Source/NStateManager/StateTransitionDynamicParameterizedAsync.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager 16 | { 17 | internal class StateTransitionDynamicParameterizedAsync 18 | : StateTransitionDynamicBase 19 | where TParam : class 20 | where TState : IComparable 21 | { 22 | public Func> StateFuncAsync { get; } 23 | 24 | public StateTransitionDynamicParameterizedAsync(Func stateAccessor 25 | , Action stateMutator 26 | , Func> stateFuncAsync 27 | , string name 28 | , uint priority) 29 | : base(stateAccessor, stateMutator, name, priority) 30 | { 31 | StateFuncAsync = stateFuncAsync ?? throw new ArgumentNullException(nameof(stateFuncAsync)); 32 | } 33 | 34 | public override async Task> ExecuteAsync(ExecutionParameters parameters 35 | , StateTransitionResult currentResult = null) 36 | { 37 | if (!(parameters.Request is TParam typeSafeParam)) 38 | { throw new ArgumentException($"Expected a {typeof(TParam).Name} parameter, but received a {parameters.Request?.GetType().Name ?? "null"}."); } 39 | 40 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context); 41 | 42 | if (parameters.CancellationToken.IsCancellationRequested) 43 | { 44 | if (currentResult != null) 45 | { return currentResult; } 46 | 47 | return new StateTransitionResult(parameters.Trigger 48 | , startState 49 | , startState 50 | , startState 51 | , lastTransitionName: string.Empty 52 | , wasCancelled: true); 53 | } 54 | 55 | var toState = await StateFuncAsync(parameters.Context, typeSafeParam, parameters.CancellationToken) 56 | .ConfigureAwait(continueOnCapturedContext: false); 57 | 58 | return ExecuteFinalizeAsync(parameters, currentResult, startState, toState); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Source/NStateManager/StateTransitionNonDynamic.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager 14 | { 15 | public abstract class StateTransitionNonDynamic : StateTransitionBase 16 | { 17 | public TState ToState { get; protected set; } 18 | 19 | protected StateTransitionNonDynamic(Func stateAccessor 20 | , Action stateMutator 21 | , TState toState 22 | , string name 23 | , uint priority) 24 | : base(stateAccessor, stateMutator, name, priority) 25 | { 26 | ToState = toState; 27 | } 28 | 29 | protected StateTransitionResult ExecutePrim(ExecutionParameters parameters 30 | , StateTransitionResult currentResult 31 | , bool conditionMet) 32 | { 33 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context); 34 | 35 | if (!conditionMet) 36 | { return GetFreshResult(parameters, currentResult, startState, wasCancelled: false, conditionMet: false, transitionDefined: true); } 37 | 38 | StateMutator(parameters.Context, ToState); 39 | var transitionResult = GetFreshResult(parameters, currentResult, startState, wasCancelled: false, conditionMet: true, transitionDefined: true); 40 | 41 | return transitionResult; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Source/NStateManager/StateTransitionResult.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | namespace NStateManager 13 | { 14 | /// 15 | /// The result of any state transition that occurred from executing a request or transition on the ./> 16 | /// 17 | /// Represents the type of state used by the transition. 18 | /// Represents the type of trigger used by the transition. 19 | public sealed class StateTransitionResult 20 | { 21 | /// 22 | /// Boolean result of the condition, if any, for the transition. 23 | /// 24 | /// Will only be False if there was a transition defined (i.e. TransitionDefined is True) and it returned False. 25 | public bool ConditionMet { get; } 26 | 27 | /// 28 | /// Current state -- after all transitions and actions have been executed. 29 | /// 30 | public TState CurrentState { get; internal set; } 31 | 32 | /// 33 | /// Name of the last transition executed. 34 | /// 35 | public string LastTransitionName { get; internal set; } 36 | 37 | /// 38 | /// State the context was in just before the current state. 39 | /// 40 | public TState PreviousState { get; internal set; } 41 | 42 | /// 43 | /// State when execution of the request or transition began. 44 | /// 45 | public TState StartingState { get; } 46 | 47 | /// 48 | /// True if there was a transition defined for the request or transition; otherwise False. 49 | /// 50 | public bool TransitionDefined { get; } 51 | 52 | /// 53 | /// Trigger that initiated the results. 54 | /// 55 | public TTrigger Trigger { get; } 56 | 57 | /// 58 | /// True if the action was cancelled; otherwise False. 59 | /// 60 | public bool WasCancelled { get; } 61 | 62 | /// 63 | /// True if the transition was successful; otherwise false. 64 | /// 65 | /// Will only be True if there was a transition defined (i.e. TransitionDefined is True) and any conditions were met. 66 | public bool WasTransitioned { get; } 67 | 68 | /// 69 | /// Constructor. 70 | /// 71 | /// The trigger that initiated the results. 72 | /// State when execution of the request or transition. 73 | /// State previous to the current state. 74 | /// Current state -- after all transitions and actions have been executed. 75 | /// 76 | /// True if there was a transition defined for the request or transition; otherwise False. 77 | /// Boolean result of the condition, if any, for the transition. 78 | /// True if the action was cancelled; otherwise False. 79 | public StateTransitionResult(TTrigger trigger 80 | , TState startingState 81 | , TState previousState 82 | , TState currentState 83 | , string lastTransitionName 84 | , bool transitionDefined = true 85 | , bool conditionMet = true 86 | , bool wasCancelled = false) 87 | { 88 | Trigger = trigger; 89 | StartingState = startingState; 90 | PreviousState = previousState; 91 | CurrentState = currentState; 92 | LastTransitionName = lastTransitionName; 93 | WasTransitioned = transitionDefined && conditionMet && !wasCancelled; 94 | TransitionDefined = transitionDefined; 95 | ConditionMet = transitionDefined && conditionMet; 96 | WasCancelled = wasCancelled; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Source/NStateManager/Sync/IStateConfigurationInternal.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | namespace NStateManager.Sync 13 | { 14 | public interface IStateConfigurationInternal : IStateConfiguration 15 | { 16 | void AddAutoTransition(TTrigger trigger, StateTransitionBase transition); 17 | void AddSuperstate(IStateConfigurationInternal superStateConfiguration); 18 | void AddTransition(TTrigger trigger, StateTransitionBase transition); 19 | StateTransitionResult ExecuteAutoTransition(ExecutionParameters parameters, StateTransitionResult currentResult); 20 | void ExecuteEntryAction(T context, StateTransitionResult currentResult); 21 | void ExecuteExitAction(T context, StateTransitionResult currentResult); 22 | void ExecuteReentryAction(T context, StateTransitionResult currentResult); 23 | StateTransitionResult FireTrigger(ExecutionParameters parameters); 24 | bool IsSubState(); 25 | bool IsSubStateOf(TState state); 26 | IStateConfigurationInternal SuperStateConfig { get; } 27 | TState State { get; } 28 | } 29 | } -------------------------------------------------------------------------------- /Source/NStateManager/Sync/IStateMachine.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager.Sync 14 | { 15 | public partial interface IStateMachine 16 | { 17 | IStateMachine AddTriggerAction(TTrigger trigger, Action action); 18 | IStateMachine AddTriggerAction(TTrigger trigger, Action action); 19 | IStateConfiguration ConfigureState(TState state); 20 | StateTransitionResult FireTrigger(T context, TTrigger trigger); 21 | StateTransitionResult FireTrigger(T context, TTrigger trigger, TRequest request) where TRequest : class; 22 | bool IsInState(T context, TState state); 23 | Func StateAccessor { get; } 24 | Action StateMutator { get; } 25 | 26 | /// 27 | /// Event raised when the context transitions to a new state when FireTrigger is called. 28 | /// 29 | event EventHandler> OnTransition; 30 | 31 | /// 32 | /// Event raised when the context doesn't transition to a new state when FireTrigger is called. 33 | /// 34 | event EventHandler> OnNoTransition; 35 | 36 | /// 37 | /// Event raised when the context's current state isn't configured for the trigger passed to FireTrigger. 38 | /// 39 | event EventHandler> OnTriggerNotConfigured; 40 | } 41 | } -------------------------------------------------------------------------------- /Source/NStateManager/Sync/StateTransition.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager.Sync 14 | { 15 | internal class StateTransition : StateTransitionNonDynamic 16 | { 17 | public Func Condition { get; } 18 | public bool HasCondition { get; } 19 | 20 | public StateTransition(Func stateAccessor, Action stateMutator, TState toState, string name, uint priority, Func condition = null) 21 | : base(stateAccessor, stateMutator, toState, name, priority) 22 | { 23 | Condition = condition; 24 | HasCondition = Condition != null; 25 | } 26 | 27 | public override StateTransitionResult Execute(ExecutionParameters parameters 28 | , StateTransitionResult currentResult = null) 29 | { 30 | return ExecutePrim(parameters, currentResult, !HasCondition || Condition(parameters.Context)); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/NStateManager/Sync/StateTransitionAutoDynamic.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager.Sync 14 | { 15 | internal class StateTransitionAutoDynamic : StateTransitionDynamicBase 16 | where TState : IComparable 17 | { 18 | private readonly IStateMachine _stateMachine; 19 | private readonly TState _startState; 20 | private readonly TState _triggerState; 21 | private readonly Func _stateFunction; 22 | 23 | public StateTransitionAutoDynamic(IStateMachine stateMachine 24 | , TState startState 25 | , Func targetStateFunction 26 | , TState triggerState 27 | , string name 28 | , uint priority) 29 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, name, priority) 30 | { 31 | _stateMachine = stateMachine; 32 | _startState = startState; 33 | _triggerState = triggerState; 34 | _stateFunction = targetStateFunction; 35 | } 36 | 37 | public override StateTransitionResult Execute(ExecutionParameters parameters 38 | , StateTransitionResult currentResult = null) 39 | { 40 | if (currentResult != null 41 | && _startState.IsEqual(currentResult.PreviousState) 42 | && (_triggerState.IsEqual(currentResult.CurrentState) 43 | || _stateMachine.IsInState(parameters.Context, _triggerState))) 44 | { 45 | StateMutator(parameters.Context, _stateFunction.Invoke(parameters.Context)); 46 | 47 | var transitioned = !StateAccessor(parameters.Context).IsEqual(_triggerState); 48 | var result = GetFreshResult(parameters 49 | , currentResult 50 | , currentResult.StartingState 51 | , wasCancelled: false 52 | , transitionDefined: true 53 | , conditionMet: transitioned); 54 | 55 | return result; 56 | } 57 | 58 | return GetFreshResult(parameters 59 | , currentResult 60 | , StateAccessor(parameters.Context) 61 | , wasCancelled: false 62 | , conditionMet: false 63 | , transitionDefined: true); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Source/NStateManager/Sync/StateTransitionAutoDynamicParameterized.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager.Sync 14 | { 15 | internal class StateTransitionAutoDynamicParameterized : StateTransitionDynamicBase 16 | where TState : IComparable 17 | where TRequest : class 18 | { 19 | private readonly IStateMachine _stateMachine; 20 | private readonly TState _startState; 21 | private readonly TState _triggerState; 22 | private readonly Func _stateFunction; 23 | 24 | public StateTransitionAutoDynamicParameterized(IStateMachine stateMachine 25 | , TState startState 26 | , Func targetStateFunction 27 | , TState triggerState 28 | , string name 29 | , uint priority) 30 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, name, priority) 31 | { 32 | _stateMachine = stateMachine; 33 | _startState = startState; 34 | _triggerState = triggerState; 35 | _stateFunction = targetStateFunction; 36 | } 37 | 38 | public override StateTransitionResult Execute(ExecutionParameters parameters 39 | , StateTransitionResult currentResult = null) 40 | { 41 | if (!(parameters.Request is TRequest typeSafeParam)) 42 | { throw new ArgumentException($"Expected a {typeof(TRequest).Name} parameter, but received a {parameters.Request?.GetType().Name ?? "null"}."); } 43 | 44 | if (currentResult != null 45 | && _startState.IsEqual(currentResult.PreviousState) 46 | && (_triggerState.IsEqual(currentResult.CurrentState) 47 | || _stateMachine.IsInState(parameters.Context, _triggerState))) 48 | { 49 | StateMutator(parameters.Context, _stateFunction(parameters.Context, typeSafeParam)); 50 | 51 | var transitioned = !StateAccessor(parameters.Context).IsEqual(_triggerState); 52 | var result = GetFreshResult(parameters 53 | , currentResult 54 | , currentResult.StartingState 55 | , wasCancelled: false 56 | , transitionDefined: true 57 | , conditionMet: transitioned); 58 | 59 | return result; 60 | } 61 | 62 | return GetFreshResult(parameters 63 | , currentResult 64 | , StateAccessor(parameters.Context) 65 | , wasCancelled: false 66 | , conditionMet: false 67 | , transitionDefined: true); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Source/NStateManager/Sync/StateTransitionAutoFallback.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager.Sync 14 | { 15 | internal class StateTransitionAutoFallback : StateTransition 16 | where TState : IComparable 17 | { 18 | private readonly IStateMachine _stateMachine; 19 | private readonly TState _startState; 20 | private readonly TState _triggerState; 21 | 22 | public StateTransitionAutoFallback(IStateMachine stateMachine 23 | , TState startState 24 | , TState triggerState 25 | , TState toState 26 | , Func condition 27 | , string name 28 | , uint priority) 29 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, name, priority, condition) 30 | { 31 | if (condition is null) 32 | { throw new ArgumentNullException(nameof(condition)); } 33 | 34 | _stateMachine = stateMachine; 35 | _startState = startState; 36 | _triggerState = triggerState; 37 | } 38 | 39 | public override StateTransitionResult Execute(ExecutionParameters parameters 40 | , StateTransitionResult currentResult = null) 41 | { 42 | if (currentResult != null 43 | && _startState.IsEqual(currentResult.PreviousState) 44 | && (_triggerState.IsEqual(currentResult.CurrentState) 45 | || _stateMachine.IsInState(parameters.Context, _triggerState))) 46 | { return base.Execute(parameters, currentResult); } 47 | 48 | return GetFreshResult(parameters 49 | , currentResult 50 | , StateAccessor(parameters.Context) 51 | , wasCancelled: false 52 | , transitionDefined: true 53 | , conditionMet: false); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Source/NStateManager/Sync/StateTransitionAutoFallbackParameterized.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager.Sync 14 | { 15 | internal class StateTransitionAutoFallbackParameterized : StateTransitionParameterized 16 | where TState : IComparable 17 | where TParam : class 18 | { 19 | private readonly IStateMachine _stateMachine; 20 | private readonly TState _startState; 21 | private readonly TState _triggerState; 22 | 23 | public StateTransitionAutoFallbackParameterized(IStateMachine stateMachine 24 | , TState startState 25 | , TState triggerState 26 | , TState toState 27 | , Func condition 28 | , string name 29 | , uint priority) 30 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, condition, name, priority) 31 | { 32 | _stateMachine = stateMachine; 33 | _startState = startState; 34 | _triggerState = triggerState; 35 | } 36 | 37 | public override StateTransitionResult Execute(ExecutionParameters parameters 38 | , StateTransitionResult currentResult = null) 39 | { 40 | if (currentResult != null 41 | && _startState.IsEqual(currentResult.PreviousState) 42 | && (_triggerState.IsEqual(currentResult.CurrentState) 43 | || _stateMachine.IsInState(parameters.Context, _triggerState))) 44 | { return base.Execute(parameters, currentResult); } 45 | 46 | return GetFreshResult(parameters 47 | , currentResult 48 | , StateAccessor(parameters.Context) 49 | , wasCancelled: false 50 | , transitionDefined: true 51 | , conditionMet: false); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Source/NStateManager/Sync/StateTransitionAutoForward.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager.Sync 14 | { 15 | internal class StateTransitionAutoForward : StateTransition 16 | where TState : IComparable 17 | { 18 | private readonly IStateMachine _stateMachine; 19 | private readonly TState _triggerState; 20 | 21 | public StateTransitionAutoForward(IStateMachine stateMachine 22 | , TState triggerState 23 | , TState toState 24 | , Func condition 25 | , string name 26 | , uint priority) 27 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, name, priority, condition) 28 | { 29 | _stateMachine = stateMachine; 30 | _triggerState = triggerState; 31 | } 32 | 33 | public override StateTransitionResult Execute(ExecutionParameters parameters 34 | , StateTransitionResult currentResult = null) 35 | { 36 | if (_stateMachine.IsInState(parameters.Context, _triggerState)) 37 | { return base.Execute(parameters, currentResult); } 38 | 39 | return GetFreshResult(parameters 40 | , currentResult 41 | , StateAccessor(parameters.Context) 42 | , wasCancelled: false 43 | , transitionDefined: true 44 | , conditionMet: false); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/NStateManager/Sync/StateTransitionAutoForwardParameterized.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager.Sync 14 | { 15 | internal class StateTransitionAutoForwardParameterized : StateTransitionParameterized 16 | where TState : IComparable 17 | where TParam : class 18 | { 19 | private readonly IStateMachine _stateMachine; 20 | private readonly TState _triggerState; 21 | 22 | public StateTransitionAutoForwardParameterized(IStateMachine stateMachine 23 | , TState triggerState 24 | , TState toState 25 | , Func condition 26 | , string name 27 | , uint priority) 28 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, condition, name, priority) 29 | { 30 | _stateMachine = stateMachine; 31 | _triggerState = triggerState; 32 | } 33 | 34 | public override StateTransitionResult Execute(ExecutionParameters parameters 35 | , StateTransitionResult currentResult = null) 36 | { 37 | if (!(parameters.Request is TParam)) 38 | { throw new ArgumentException($"Expected a {typeof(TParam).Name} parameter, but received a {parameters.Request?.GetType().Name ?? "null"}."); } 39 | 40 | if (currentResult != null 41 | && (_triggerState.IsEqual(currentResult.CurrentState) 42 | || _stateMachine.IsInState(parameters.Context, _triggerState))) 43 | { return base.Execute(parameters, currentResult); } 44 | 45 | return GetFreshResult(parameters 46 | , currentResult 47 | , StateAccessor(parameters.Context) 48 | , wasCancelled: false 49 | , transitionDefined: true 50 | , conditionMet: false); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Source/NStateManager/Sync/StateTransitionParameterized.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager.Sync 14 | { 15 | internal class StateTransitionParameterized : StateTransitionNonDynamic 16 | where TParam : class 17 | { 18 | public Func Condition { get; } 19 | 20 | public StateTransitionParameterized(Func stateAccessor, Action stateMutator, TState toState, Func condition, string name, uint priority) 21 | : base(stateAccessor, stateMutator, toState, name, priority) 22 | { Condition = condition ?? throw new ArgumentNullException(nameof(condition)); } 23 | 24 | public override StateTransitionResult Execute(ExecutionParameters parameters 25 | , StateTransitionResult currentResult = null) 26 | { 27 | if (!(parameters.Request is TParam typeSafeParam)) 28 | { throw new ArgumentException($"Expected a {typeof(TParam).Name} parameter, but received a {parameters.Request?.GetType().Name ?? "null"}."); } 29 | 30 | return ExecutePrim(parameters, currentResult, Condition(parameters.Context, typeSafeParam)); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Source/NStateManager/TransitionEventArgs.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager 14 | { 15 | public class TransitionEventArgs : EventArgs 16 | { 17 | /// 18 | /// Parameters used for the call to FireTrigger. 19 | /// 20 | public ExecutionParameters Parameters { get; } 21 | 22 | /// 23 | /// Details of transitions that occurred during FireTrigger. 24 | /// 25 | public StateTransitionResult TransitionResult { get; } 26 | 27 | /// 28 | /// Constructor. 29 | /// 30 | /// Parameters used for the call to FireTrigger. 31 | /// Details of transitions that occurred during FireTrigger. 32 | public TransitionEventArgs(ExecutionParameters parameters, StateTransitionResult transitionResult) 33 | { 34 | Parameters = parameters ?? throw new ArgumentNullException(nameof(parameters)); 35 | TransitionResult = transitionResult ?? throw new ArgumentNullException(nameof(transitionResult)); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Source/NStateManager/TriggerAction.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager 14 | { 15 | internal class TriggerAction : TriggerActionBase 16 | { 17 | internal Action Action { get; } 18 | 19 | internal TriggerAction(Action action) 20 | { 21 | Action = action ?? throw new ArgumentNullException(nameof(action)); 22 | } 23 | 24 | internal override void Execute(ExecutionParameters parameters) 25 | { 26 | Action.Invoke(parameters.Context); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Source/NStateManager/TriggerActionBase.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager 12 | { 13 | internal abstract class TriggerActionBase 14 | { 15 | internal abstract void Execute(ExecutionParameters parameters); 16 | } 17 | } -------------------------------------------------------------------------------- /Source/NStateManager/TriggerActionFactory.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace NStateManager 16 | { 17 | internal static class TriggerActionFactory 18 | { 19 | internal static TriggerActionBase GetTriggerAction(Action action) 20 | { 21 | return new TriggerAction(action); 22 | } 23 | 24 | internal static TriggerActionBase GetTriggerAction(Action action) 25 | { 26 | return new TriggerActionParameterized(action); 27 | } 28 | 29 | internal static FunctionActionBase GetTriggerAction(Func action) 30 | { 31 | return new FunctionAction(action); 32 | } 33 | 34 | internal static FunctionActionBase GetTriggerAction(Func action) 35 | { 36 | return new FunctionActionParameterized(action); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /Source/NStateManager/TriggerActionParameterized.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | 13 | namespace NStateManager 14 | { 15 | internal class TriggerActionParameterized : TriggerActionBase 16 | { 17 | internal Action Action { get; } 18 | 19 | internal TriggerActionParameterized(Action action) 20 | { 21 | Action = action ?? throw new ArgumentNullException(nameof(action)); 22 | } 23 | 24 | internal override void Execute(ExecutionParameters parameters) 25 | { 26 | if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } 27 | 28 | if (!(parameters.Request is TRequest typedRequestType)) 29 | { throw new ArgumentException($"{nameof(parameters.Request)} must be of type {typeof(TRequest).Name}."); } 30 | 31 | Action.Invoke(parameters.Context, typedRequestType); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/ExecutionParametersTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | using Xunit; 13 | 14 | namespace NStateManager.Tests 15 | { 16 | public class ExecutionParametersTests 17 | { 18 | [Fact] 19 | public void Constructor_sets_properties() 20 | { 21 | var sut = new ExecutionParameters("trigger", context: 4, request: "Scott"); 22 | 23 | Assert.Equal(expected: 4, actual: sut.Context); 24 | Assert.Equal("Scott", sut.Request); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Export/CsvExporterTests.cs: -------------------------------------------------------------------------------- 1 | using NStateManager.Export; 2 | using NStateManager.Sync; 3 | using System; 4 | using TelephoneCallExample; 5 | using Xunit; 6 | 7 | namespace NStateManager.Tests.Export 8 | { 9 | public class CsvExporterTests 10 | { 11 | [Fact] 12 | public void Export_exports_phone_graph() 13 | { 14 | var stateMachine = new PhoneCall("Scott"); 15 | var summary = stateMachine.GetSummary(); 16 | var csv = CsvExporter.Export(summary); 17 | } 18 | 19 | [Fact] 20 | public void Export_exports_sale_graph() 21 | { 22 | var stateMachine = GetSaleStateMachine(); 23 | var summary = stateMachine.GetSummary(); 24 | var csv = CsvExporter.Export(summary); 25 | } 26 | 27 | private StateMachine GetSaleStateMachine() 28 | { 29 | var sut = new StateMachine( 30 | stateAccessor: sale2 => sale2.State 31 | , stateMutator: (sale3, newState) => sale3.State = newState); 32 | #pragma warning disable 219 33 | var entryActionFired = false; 34 | #pragma warning restore 219 35 | 36 | sut.ConfigureState(SaleState.Open) 37 | .AddTransition(SaleEvent.Pay, SaleState.Complete, (sale => Math.Abs(sale.Balance) < 0.01)) 38 | .AddTransition(SaleEvent.Pay, SaleState.ChangeDue, (sale => sale.Balance < 0)); 39 | 40 | sut.ConfigureState(SaleState.Complete) 41 | .AddEntryAction(sale1 => entryActionFired = true); 42 | 43 | return sut; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Export/StateMachineAsyncTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using NStateManager.Async; 12 | using System; 13 | using System.Threading.Tasks; 14 | using Xunit; 15 | 16 | namespace NStateManager.Tests.Export 17 | { 18 | public class StateMachineAsyncTests 19 | { 20 | [Fact] 21 | public void GetSummary_includes_all_states() 22 | { 23 | var sut = getTestStateMachine(); 24 | 25 | var summary = sut.GetSummary(); 26 | 27 | Assert.Contains(summary.StateDetails, s => s.State == SaleState.Open); 28 | Assert.Contains(summary.StateDetails, s => s.State == SaleState.ChangeDue); 29 | Assert.Contains(summary.StateDetails, s => s.State == SaleState.Complete); 30 | } 31 | 32 | [Fact] 33 | public void GetSummary_includes_all_transitions() 34 | { 35 | var sut = getTestStateMachine(); 36 | 37 | var summary = sut.GetSummary(); 38 | 39 | Assert.Equal(3, summary.Transitions.Count); 40 | Assert.Contains(summary.Transitions, t => t.FromState.State.IsEqual(SaleState.Open) && t.ToState.State.IsEqual(SaleState.Complete) && t.HasCondition); 41 | Assert.Contains(summary.Transitions, t => t.FromState.State.IsEqual(SaleState.Open) && t.ToState.State.IsEqual(SaleState.ChangeDue) && t.HasCondition); 42 | Assert.Contains(summary.Transitions, t => t.FromState.State.IsEqual(SaleState.ChangeDue) && t.ToState.State.IsEqual(SaleState.Complete) && !t.HasCondition); 43 | } 44 | 45 | [Fact] 46 | public void GetSummary_includes_all_final_states() 47 | { 48 | var sut = getTestStateMachine(); 49 | 50 | var summary = sut.GetSummary(); 51 | 52 | Assert.Contains(summary.FinalStates, s => s.State == SaleState.Complete); 53 | } 54 | 55 | [Fact] 56 | public void GetSummary_includes_all_starting_states() 57 | { 58 | var sut = getTestStateMachine(); 59 | 60 | var summary = sut.GetSummary(); 61 | 62 | Assert.Contains(summary.StartingStates, s => s.State == SaleState.Open); 63 | } 64 | 65 | private StateMachine getTestStateMachine() 66 | { 67 | var stateMachine = new StateMachine( 68 | stateAccessor: sale => sale.State 69 | , stateMutator: (sale, newState) => sale.State = newState); 70 | 71 | stateMachine.ConfigureState(SaleState.Open) 72 | .AddTransition(SaleEvent.Pay, SaleState.Complete, (sale1, _) => Task.FromResult(Math.Abs(sale1.Balance) < 0.00001)) 73 | .AddTransition(SaleEvent.Pay, SaleState.ChangeDue, (sale2, _) => Task.FromResult(sale2.Balance < 0)); 74 | stateMachine.ConfigureState(SaleState.ChangeDue) 75 | .AddTransition(SaleEvent.ChangeGiven, SaleState.Complete); 76 | 77 | return stateMachine; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Export/StateMachineTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | using System; 13 | using NStateManager.Sync; 14 | using Xunit; 15 | 16 | namespace NStateManager.Tests.Export 17 | { 18 | public class StateMachineTests 19 | { 20 | [Fact] 21 | public void GetSummary_includes_all_states() 22 | { 23 | var sut = getTestStateMachine(); 24 | 25 | var summary = sut.GetSummary(); 26 | 27 | Assert.Contains(summary.StateDetails, s => s.State == SaleState.Open); 28 | Assert.Contains(summary.StateDetails, s => s.State == SaleState.ChangeDue); 29 | Assert.Contains(summary.StateDetails, s => s.State == SaleState.Complete); 30 | } 31 | 32 | [Fact] 33 | public void GetSummary_includes_all_transitions() 34 | { 35 | var sut = getTestStateMachine(); 36 | 37 | var summary = sut.GetSummary(); 38 | 39 | Assert.Equal(3, summary.Transitions.Count); 40 | Assert.Contains(summary.Transitions, t => t.FromState.State.CompareTo(SaleState.Open) == 0 && t.ToState.State.CompareTo(SaleState.Complete) == 0 && t.HasCondition); 41 | Assert.Contains(summary.Transitions, t => t.FromState.State.CompareTo(SaleState.Open) == 0 && t.ToState.State.CompareTo(SaleState.ChangeDue) == 0 && t.HasCondition); 42 | Assert.Contains(summary.Transitions, t => t.FromState.State.CompareTo(SaleState.ChangeDue) == 0 && t.ToState.State.CompareTo(SaleState.Complete) == 0 && !t.HasCondition); 43 | } 44 | 45 | [Fact] 46 | public void GetSummary_includes_all_final_states() 47 | { 48 | var sut = getTestStateMachine(); 49 | 50 | var summary = sut.GetSummary(); 51 | 52 | Assert.Contains(summary.FinalStates, s => s.State == SaleState.Complete); 53 | } 54 | 55 | [Fact] 56 | public void GetSummary_includes_all_starting_states() 57 | { 58 | var sut = getTestStateMachine(); 59 | 60 | var summary = sut.GetSummary(); 61 | 62 | Assert.Contains(summary.StartingStates, s => s.State == SaleState.Open); 63 | } 64 | 65 | private StateMachine getTestStateMachine() 66 | { 67 | var stateMachine = new StateMachine( 68 | stateAccessor: sale => sale.State 69 | , stateMutator: (sale, newState) => sale.State = newState); 70 | 71 | stateMachine.ConfigureState(SaleState.Open) 72 | .AddTransition(SaleEvent.Pay, SaleState.Complete, sale1 => Math.Abs(sale1.Balance) < 0.00001) 73 | .AddTransition(SaleEvent.Pay, SaleState.ChangeDue, sale2 => sale2.Balance < 0); 74 | stateMachine.ConfigureState(SaleState.ChangeDue) 75 | .AddTransition(SaleEvent.ChangeGiven, SaleState.Complete); 76 | 77 | return stateMachine; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Export/StateTransitionDetailsTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using NSimpleTester; 12 | using NStateManager.Export; 13 | using Xunit; 14 | 15 | namespace NStateManager.Tests.Export 16 | { 17 | public class StateTransitionDetailsTests 18 | { 19 | [Fact] 20 | public void Properties_work() 21 | { 22 | var classTester = new ClassTester(new TransitionDetails("test" 23 | , new StateDetails(SaleState.Open) 24 | , SaleEvent.AddItem)); 25 | classTester.TestConstructors(); 26 | //classTester.TestEquality(); 27 | classTester.TestProperties(); 28 | 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/FunctionActionParameterizedTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | using System; 13 | using System.Threading; 14 | using System.Threading.Tasks; 15 | using Xunit; 16 | 17 | namespace NStateManager.Tests 18 | { 19 | public class FunctionActionParameterizedTests 20 | { 21 | [Fact] 22 | public void Constructor_throws_ArgumentNullException_if_Action_null() 23 | { 24 | Assert.Throws(() => new FunctionActionParameterized(action: null)); 25 | } 26 | 27 | [Fact] 28 | public async Task ExecuteAsync_executes_Action() 29 | { 30 | const double updatedBalance = 1.23; 31 | var sale = new Sale(saleId: 5) { Balance = 0 }; 32 | var sut = new FunctionActionParameterized((sale1, sale2, _) => Task.FromResult(sale1.Balance = updatedBalance)); 33 | 34 | await sut.ExecuteAsync(sale, request: sale, cancellationToken: default); 35 | 36 | Assert.Equal(updatedBalance, sale.Balance); 37 | } 38 | 39 | [Fact] 40 | public void ExecuteAsync_can_be_cancelled() 41 | { 42 | using var cancellationSource = new CancellationTokenSource(); 43 | var sale = new Sale(saleId: 3); 44 | 45 | var sut = new FunctionActionParameterized((_, sale2, cancelToken) => 46 | Task.Delay(millisecondsDelay: 999999, cancellationToken: cancelToken)); 47 | 48 | var funcTask = Task.Run(async () => await sut.ExecuteAsync(sale, cancellationSource.Token, request: sale), cancellationSource.Token); 49 | 50 | cancellationSource.Cancel(); 51 | Task.Delay(millisecondsDelay: 67).Wait(); 52 | 53 | Assert.True(funcTask.IsCanceled); 54 | } 55 | 56 | [Fact] 57 | public async Task ExecuteAsync_throws_ArgumentException_if_request_wrong_Type() 58 | { 59 | const double updatedBalance = 1.23; 60 | var sale = new Sale(saleId: 5) { Balance = 0 }; 61 | var sut = new FunctionActionParameterized((sale1, sale2, _) => Task.FromResult(sale1.Balance = updatedBalance)); 62 | 63 | await Assert.ThrowsAsync(() => sut.ExecuteAsync(sale, request: "wrongType", cancellationToken: default)); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/FunctionActionTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | using System; 13 | using System.Threading; 14 | using System.Threading.Tasks; 15 | using Xunit; 16 | 17 | namespace NStateManager.Tests 18 | { 19 | public class FunctionActionTests 20 | { 21 | [Fact] 22 | public void Constructor_throws_ArgumentNullException_if_Action_null() 23 | { 24 | Assert.Throws(() => new FunctionAction(action: null)); 25 | } 26 | 27 | [Fact] 28 | public async Task ExecuteAsync_executes_Action() 29 | { 30 | const double updatedBalance = 1.23; 31 | var sale = new Sale(saleId: 5) { Balance = 0 }; 32 | var sut = new FunctionAction((sale1, _) => Task.FromResult(sale1.Balance = updatedBalance)); 33 | 34 | await sut.ExecuteAsync(sale, request: null, cancellationToken: default); 35 | 36 | Assert.Equal(updatedBalance, sale.Balance); 37 | } 38 | 39 | [Fact] 40 | public void ExecuteAsync_can_be_cancelled() 41 | { 42 | using var cancellationSource = new CancellationTokenSource(); 43 | var sale = new Sale(saleId: 3); 44 | var wasCancelled = false; 45 | 46 | var mutex = new Mutex(initiallyOwned: false); 47 | var sut = new FunctionAction((_, cancelToken) => 48 | { 49 | mutex.WaitOne(); 50 | do 51 | { 52 | Task.Delay(millisecondsDelay: 123).Wait(); 53 | } while (!cancelToken.IsCancellationRequested); 54 | 55 | mutex.ReleaseMutex(); 56 | wasCancelled = true; 57 | return Task.FromCanceled(cancelToken); 58 | }); 59 | 60 | Task.Factory.StartNew(async () => 61 | { 62 | await sut.ExecuteAsync(sale, cancellationSource.Token, request: null); 63 | }, TaskCreationOptions.LongRunning); 64 | 65 | try 66 | { 67 | Task.Delay(555).Wait(); 68 | cancellationSource.Cancel(); 69 | } 70 | catch 71 | { 72 | cancellationSource.Cancel(); 73 | } 74 | 75 | mutex.WaitOne(); 76 | 77 | Assert.True(wasCancelled); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/NStateManager.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net5.0-windows 4 | Library 5 | true 6 | true 7 | false 8 | 9 | 10 | 11 | true 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | all 29 | runtime; build; native; contentfiles; analyzers; buildtransitive 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | all 53 | runtime; build; native; contentfiles; analyzers; buildtransitive 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Phone.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Tests 12 | { 13 | public class Phone 14 | { 15 | public string Name { get; set; } 16 | public PhoneState State { get; set; } 17 | } 18 | 19 | public enum PhoneState 20 | { 21 | OnHook, 22 | InRinging, 23 | OffHook, 24 | ReadyToDial, 25 | Dialing, 26 | GettingCallerStatus, 27 | OutRinging, 28 | Busy, 29 | Connected, 30 | OnHold, 31 | Recording, 32 | OutOfService 33 | } 34 | 35 | public enum PhoneEvent 36 | { 37 | IncomingCall, 38 | Answer, 39 | PickUp, 40 | Dialing, 41 | DialingDone, 42 | HangUp, 43 | CallerBusy, 44 | Ringing, 45 | CallerPickedUp, 46 | PutOnHold, 47 | RemoveHold, 48 | StartRecording, 49 | StopRecording, 50 | RemoveFromService, 51 | ReturnToService, 52 | LineDisruption 53 | } 54 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | using System.Reflection; 13 | using System.Runtime.InteropServices; 14 | 15 | // General Information about an assembly is controlled through the following 16 | // set of attributes. Change these attribute values to modify the information 17 | // associated with an assembly. 18 | [assembly: AssemblyTitle("NStateManager.Tests")] 19 | [assembly: AssemblyDescription("")] 20 | [assembly: AssemblyConfiguration("")] 21 | [assembly: AssemblyCompany("")] 22 | [assembly: AssemblyProduct("NStateManager.Tests")] 23 | [assembly: AssemblyCopyright("Copyright © 2017")] 24 | [assembly: AssemblyTrademark("")] 25 | [assembly: AssemblyCulture("")] 26 | 27 | // Setting ComVisible to false makes the types in this assembly not visible 28 | // to COM components. If you need to access a type in this assembly from 29 | // COM, set the ComVisible attribute to true on that type. 30 | [assembly: ComVisible(visibility: false)] 31 | 32 | // The following GUID is for the ID of the typelib if this project is exposed to COM 33 | [assembly: Guid("2addf230-a48b-462b-a1d4-cd91d9ec5f6f")] 34 | 35 | // Version information for an assembly consists of the following four values: 36 | // 37 | // Major Version 38 | // Minor Version 39 | // Build Number 40 | // Revision 41 | // 42 | // You can specify all the values or you can default the Build and Revision Numbers 43 | // by using the '*' as shown below: 44 | // [assembly: AssemblyVersion("1.0.*")] 45 | [assembly: AssemblyVersion("1.0.0.0")] 46 | [assembly: AssemblyFileVersion("1.0.0.0")] 47 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Sale.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Tests 12 | { 13 | public enum SaleState 14 | { 15 | Open, 16 | ChangeDue, 17 | Complete 18 | } 19 | 20 | public enum SaleEvent 21 | { 22 | AddItem, 23 | Pay, 24 | ChangeGiven 25 | } 26 | 27 | public class Sale 28 | { 29 | public double Balance { get; set; } 30 | public SaleState State { get; set; } 31 | public int SaleId { get; } 32 | 33 | public Sale(int saleId) 34 | { 35 | SaleId = saleId; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/SaleComplex.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Tests 12 | { 13 | public class SaleComplex 14 | { 15 | public SaleComplexState State { get; internal set; } 16 | } 17 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/SaleComplexEvent.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Tests 12 | { 13 | public enum SaleComplexEvent 14 | { 15 | AddItem, 16 | SetItemQuantity, 17 | Pay, 18 | GiveChange, 19 | Cancel 20 | } 21 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/SaleComplexState.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | namespace NStateManager.Tests 12 | { 13 | public enum SaleComplexState 14 | { 15 | Open, 16 | Overpaid, 17 | Finalized, 18 | Cancelled, 19 | Paid 20 | } 21 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/SimpleTests.cs: -------------------------------------------------------------------------------- 1 | using NSimpleTester; 2 | using Xunit; 3 | 4 | namespace NStateManager.Tests 5 | { 6 | public class SimpleTests 7 | { 8 | [Fact] 9 | public void Simple_Tests() 10 | { 11 | new AssemblyTester(typeof(FunctionAction<>).Assembly) 12 | .ExcludeClass("ExecutionParameters`2") 13 | .ExcludeClass("FunctionAction`1") 14 | .ExcludeClass("FunctionActionBase`1") 15 | .ExcludeClass("FunctionActionParameterized`2") 16 | .ExcludeClass("StateConfigurationBase`3") 17 | .ExcludeClass("StateTransitionBase`3") 18 | .ExcludeClass("StateTransitionDynamic`3") 19 | .ExcludeClass("StateTransitionDynamicBase`3") 20 | .ExcludeClass("StateTransitionDynamicParameterized`4") 21 | .ExcludeClass("StateTransitionNonDynamic`3") 22 | .ExcludeClass("StateTransitionResult`2") 23 | .ExcludeClass("TransitionEventArgs`3") 24 | .ExcludeClass("TriggerAction`2") 25 | .ExcludeClass("TriggerActionBase`2") 26 | .ExcludeClass("TriggerActionParameterized`3") 27 | .ExcludeClass("StateMachine`3") 28 | .ExcludeClass("StateConfiguration`3") 29 | .ExcludeClass("StateTransition`3") 30 | .ExcludeClass("StateTransitionAutoDynamic`3") 31 | .ExcludeClass("StateTransitionAutoDynamicParameterized`4") 32 | .ExcludeClass("StateTransitionAutoFallback`3") 33 | .ExcludeClass("StateTransitionAutoFallbackParameterized`4") 34 | .ExcludeClass("StateTransitionAutoForward`3") 35 | .ExcludeClass("StateTransitionAutoForwardParameterized`4") 36 | .ExcludeClass("StateTransitionParameterized`4") 37 | .ExcludeClass("ConfigurationSummary`2") 38 | .ExcludeClass("CsvExporter`2") 39 | .ExcludeClass("DotGraphExporter`2") 40 | .ExcludeClass("StateDetails`2") 41 | .ExcludeClass("TransitionDetails`2") 42 | .ExcludeClass("NStateManager.Async.StateConfiguration`3", isFullName: true) 43 | .ExcludeClass("NStateManager.Async.StateMachine`3", isFullName: true) 44 | .ExcludeClass("NStateManager.Async.StateTransition`3", isFullName: true) 45 | .ExcludeClass("NStateManager.Async.StateTransitionAutoDynamic`3", isFullName: true) 46 | .ExcludeClass("NStateManager.Async.StateTransitionAutoDynamicParameterized`4", isFullName: true) 47 | .ExcludeClass("NStateManager.Async.StateTransitionAutoFallback`3", isFullName: true) 48 | .ExcludeClass("StateTransitionAutoFallbackParameterized`4") 49 | .ExcludeClass("NStateManager.Async.StateTransitionAutoForward`3", isFullName: true) 50 | .ExcludeClass("NStateManager.Async.StateTransitionAutoFallbackParameterized`4", isFullName: true) 51 | .ExcludeClass("NStateManager.Async.StateTransitionAutoForwardParameterized`4", isFullName: true) 52 | .ExcludeClass("NStateManager.Async.StateTransitionParameterized`4", isFullName: true) 53 | .TestAssembly(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/StateTransitionBaseTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | using System; 13 | using System.Threading.Tasks; 14 | using Xunit; 15 | 16 | namespace NStateManager.Tests 17 | { 18 | public class StateTransitionBaseTests 19 | { 20 | public class StateTransitionTester : StateTransitionNonDynamic 21 | { 22 | public StateTransitionTester(Func stateAccessor, Action stateMutator, TState toState, string name, uint priority) 23 | : base(stateAccessor, stateMutator, toState, name, priority) 24 | {} 25 | 26 | public Func GetStateAccessor() { return StateAccessor; } 27 | public Action GetStateMutator() { return StateMutator; } 28 | public string GetName() { return Name; } 29 | } 30 | 31 | [Fact] 32 | public void Constructor_throws_ArgumentNullException_if_stateAccessor_null() 33 | { 34 | Assert.Throws(() => new StateTransitionTester(stateAccessor: null 35 | , stateMutator: (sale, newState) => sale.State = newState 36 | , toState: SaleState.Complete 37 | , name: "tester" 38 | , priority: 1)); 39 | } 40 | 41 | [Fact] 42 | public void Constructor_throws_ArgumentNullException_if_stateMutator_null() 43 | { 44 | Assert.Throws(() => new StateTransitionTester(stateAccessor: sale => sale.State 45 | , stateMutator: null 46 | , toState: SaleState.Complete 47 | , name: "tester" 48 | , priority: 1)); 49 | } 50 | 51 | [Fact] 52 | public void Constructor_sets_properties() 53 | { 54 | SaleState StateAccessor(Sale sale) => sale.State; 55 | void StateMutator(Sale sale, SaleState newState) => sale.State = newState; 56 | var toState = SaleState.ChangeDue; 57 | var name = "testName"; 58 | uint priority = 97; 59 | 60 | var sut = new StateTransitionTester(StateAccessor 61 | , StateMutator 62 | , toState 63 | , name 64 | , priority); 65 | 66 | Assert.Equal(StateAccessor, sut.GetStateAccessor()); 67 | Assert.Equal(StateMutator, sut.GetStateMutator()); 68 | Assert.Equal(toState, sut.ToState); 69 | Assert.Equal(name, sut.GetName()); 70 | Assert.Equal(priority, sut.Priority); 71 | } 72 | 73 | [Fact] 74 | public void Execute_throws_NotImplementedException() 75 | { 76 | var sut = new StateTransitionTester(stateAccessor: sale => sale.State 77 | , stateMutator: (sale, newState) => sale.State = newState 78 | , toState: SaleState.Complete 79 | , name: "tester" 80 | , priority: 1); 81 | 82 | Assert.Throws(() => sut.Execute(parameters: null)); 83 | } 84 | 85 | [Fact] 86 | public async Task ExecuteAsync_throws_NotImplementedException() 87 | { 88 | var sut = new StateTransitionTester(stateAccessor: sale => sale.State 89 | , stateMutator: (sale, newState) => sale.State = newState 90 | , toState: SaleState.Complete 91 | , name: "tester" 92 | , priority: 1); 93 | 94 | await Assert.ThrowsAsync(() => sut.ExecuteAsync(parameters: null)); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/StateTransitionParameterizedTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using NStateManager.Sync; 12 | using System; 13 | using Xunit; 14 | 15 | namespace NStateManager.Tests 16 | { 17 | public class StateTransitionParameterizedTests 18 | { 19 | [Fact] 20 | public void Constructor_throws_ArgumentNullException_if_Condition_is_null() 21 | { 22 | Assert.Throws(() => new StateTransitionParameterized( 23 | stateAccessor: sale => sale.State 24 | , stateMutator: (sale, newState) => sale.State = newState 25 | , toState: SaleState.Complete 26 | , condition: null 27 | , name: "test" 28 | , priority: 1)); 29 | } 30 | 31 | [Fact] 32 | public void Execute_throws_ArgumentException_if_parameter_wrong_type() 33 | { 34 | var sut = new StateTransitionParameterized( 35 | stateAccessor: sale => sale.State 36 | , stateMutator: (sale, newState) => sale.State = newState 37 | , toState: SaleState.Complete 38 | , condition: (_, parameter) => parameter == "yes" 39 | , name: "test" 40 | , priority: 1); 41 | 42 | Assert.Throws(() => sut.Execute(new ExecutionParameters(SaleEvent.Pay, new Sale(saleId: 9), request: 0))); 43 | } 44 | 45 | [Fact] 46 | public void Execute_transitions_if_condition_met() 47 | { 48 | var testSale = new Sale(saleId: 55) { State = SaleState.Open} ; 49 | 50 | var sut = new StateTransitionParameterized( 51 | stateAccessor: sale => sale.State 52 | , stateMutator: (sale, newState) => sale.State = newState 53 | , toState: SaleState.Complete 54 | , condition: (_, parameter) => parameter == "yes" 55 | , name: "test" 56 | , priority: 1); 57 | 58 | sut.Execute(new ExecutionParameters(SaleEvent.Pay, testSale, request: "yes")); 59 | 60 | Assert.Equal(SaleState.Complete, testSale.State); 61 | } 62 | 63 | [Fact] 64 | public void Execute_does_not_transition_if_condition_not_met() 65 | { 66 | var testSale = new Sale(saleId: 55) { State = SaleState.Open }; 67 | var sut = new StateTransitionParameterized( 68 | stateAccessor: sale => sale.State 69 | , stateMutator: (sale, newState) => sale.State = newState 70 | , toState: SaleState.Complete 71 | , condition: (_, parameter) => parameter == "yes" 72 | , name: "test" 73 | , priority: 1); 74 | 75 | sut.Execute(new ExecutionParameters(SaleEvent.Pay, testSale, request: "NO")); 76 | 77 | Assert.Equal(SaleState.Open, testSale.State); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/StateTransitionTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | using NStateManager.Sync; 13 | using Xunit; 14 | 15 | namespace NStateManager.Tests 16 | { 17 | public class StateTransitionTests 18 | { 19 | [Fact] 20 | public void Execute_changes_state_if_condition_met() 21 | { 22 | const SaleState startState = SaleState.Open; 23 | const SaleState endState = SaleState.Complete; 24 | var sale = new Sale(saleId: 66) { State = startState }; 25 | 26 | var sut = new StateTransition( 27 | stateAccessor: saleToUpdate => saleToUpdate.State 28 | , stateMutator: (saleToUpdate, newState) => saleToUpdate.State = newState 29 | , toState: endState 30 | , condition: _ => true 31 | , name: "test" 32 | , priority: 1); 33 | 34 | var result = sut.Execute(new ExecutionParameters(SaleEvent.Pay, sale)); 35 | 36 | Assert.True(result.WasTransitioned); 37 | Assert.Equal(endState, sale.State); 38 | } 39 | 40 | [Fact] 41 | public void Execute_does_not_change_state_if_condition_not_met() 42 | { 43 | const SaleState startState = SaleState.Open; 44 | const SaleState endState = SaleState.Complete; 45 | var sale = new Sale(saleId: 66) { State = startState }; 46 | 47 | var sut = new StateTransition( 48 | stateAccessor: saleToUpdate => saleToUpdate.State 49 | , stateMutator: (saleToUpdate, newState) => saleToUpdate.State = newState 50 | , toState: endState 51 | , condition: _ => false 52 | , name: "test" 53 | , priority: 1); 54 | 55 | var result = sut.Execute(new ExecutionParameters(SaleEvent.Pay, sale)); 56 | 57 | Assert.False(result.WasTransitioned); 58 | Assert.Equal(startState, sale.State); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Sync/StateTransitionAutoFallbackParameterizedTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using NStateManager.Sync; 12 | using System; 13 | using Xunit; 14 | 15 | namespace NStateManager.Tests.Sync 16 | { 17 | public class StateTransitionAutoFallbackParameterizedTests 18 | { 19 | [Fact] 20 | public void Constructor_throws_ArgumentNullException_if_Condition_is_null() 21 | { 22 | Assert.Throws(() => new StateTransitionAutoFallbackParameterized( 23 | GetStateMachine() 24 | , SaleState.Complete 25 | , SaleState.Complete 26 | , SaleState.Open 27 | , condition: null 28 | , name: "test" 29 | , priority: 1)); 30 | } 31 | 32 | [Fact] 33 | public void Execute_throws_ArgumentException_if_parameter_wrong_type() 34 | { 35 | var sut = new StateTransitionAutoFallbackParameterized( 36 | GetStateMachine() 37 | , SaleState.Complete 38 | , SaleState.Complete 39 | , SaleState.Open 40 | , condition: (_, parameter) => parameter == "yes" 41 | , name: "test" 42 | , priority: 1); 43 | 44 | Assert.Throws(() => sut.Execute(new ExecutionParameters(SaleEvent.Pay, new Sale(saleId: 9), request: 0) 45 | , new StateTransitionResult(SaleEvent.Pay 46 | , SaleState.Complete 47 | , SaleState.Complete 48 | , SaleState.Complete 49 | , "transactionName"))); 50 | } 51 | 52 | [Fact] 53 | public void Execute_transitions_if_condition_met() 54 | { 55 | var testSale = new Sale(saleId: 55) { State = SaleState.ChangeDue} ; 56 | 57 | var sut = new StateTransitionAutoFallbackParameterized( 58 | GetStateMachine() 59 | , SaleState.ChangeDue 60 | , SaleState.ChangeDue 61 | , SaleState.Complete 62 | , condition: (_, parameter) => parameter == "yes" 63 | , name: "test" 64 | , priority: 1); 65 | 66 | sut.Execute(new ExecutionParameters(SaleEvent.Pay, testSale, request: "yes") 67 | , new StateTransitionResult(SaleEvent.Pay 68 | , SaleState.ChangeDue 69 | , SaleState.ChangeDue 70 | , SaleState.ChangeDue 71 | , "transactionName")); 72 | 73 | Assert.Equal(SaleState.Complete, testSale.State); 74 | } 75 | 76 | [Fact] 77 | public void Execute_does_not_transition_if_condition_not_met() 78 | { 79 | var testSale = new Sale(saleId: 55) { State = SaleState.Open }; 80 | 81 | var sut = new StateTransitionAutoFallbackParameterized( 82 | GetStateMachine() 83 | , SaleState.Complete 84 | , SaleState.Complete 85 | , SaleState.Open 86 | , condition: (_, parameter) => parameter == "yes" 87 | , name: "test" 88 | , priority: 1); 89 | 90 | sut.Execute(new ExecutionParameters(SaleEvent.Pay, testSale, request: "NO"), GetDummyResult()); 91 | 92 | Assert.Equal(SaleState.Open, testSale.State); 93 | } 94 | 95 | private StateTransitionResult GetDummyResult() 96 | { 97 | return new StateTransitionResult(SaleEvent.Pay 98 | , SaleState.Open 99 | , SaleState.Open 100 | , SaleState.Open 101 | , "transactionName"); 102 | } 103 | 104 | private IStateMachine GetStateMachine() 105 | { 106 | return new StateMachine(saleToUpdate => saleToUpdate.State 107 | , (saleToUpdate, newState) => saleToUpdate.State = newState); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Sync/StateTransitionAutoFallbackTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using NStateManager.Sync; 12 | using System; 13 | using Xunit; 14 | 15 | namespace NStateManager.Tests.Sync 16 | { 17 | public class StateTransitionAutoFallbackTests 18 | { 19 | [Fact] 20 | public void Constructor_throws_ArgumentNullException_if_condition_null() 21 | { 22 | Assert.Throws(() => new StateTransitionAutoFallback( 23 | GetStateMachine() 24 | , SaleState.Open 25 | , triggerState: SaleState.Open 26 | , toState: SaleState.Open 27 | , condition: null 28 | , name: "test" 29 | , priority: 1)); 30 | } 31 | 32 | [Fact] 33 | public void Execute_changes_state_if_condition_met() 34 | { 35 | const SaleState startState = SaleState.Open; 36 | const SaleState endState = SaleState.Complete; 37 | var sale = new Sale(saleId: 66) { State = startState }; 38 | 39 | var sut = new StateTransitionAutoFallback( 40 | GetStateMachine() 41 | , startState 42 | , toState: endState 43 | , condition: _ => true 44 | , triggerState: startState 45 | , name: "test" 46 | , priority: 1); 47 | 48 | var result = sut.Execute(new ExecutionParameters(SaleEvent.Pay, sale), GetDummyResult()); 49 | 50 | Assert.True(result.WasTransitioned); 51 | Assert.Equal(endState, sale.State); 52 | } 53 | 54 | [Fact] 55 | public void Execute_does_not_change_state_if_condition_not_met() 56 | { 57 | const SaleState startState = SaleState.Open; 58 | const SaleState endState = SaleState.Complete; 59 | var sale = new Sale(saleId: 66) { State = startState }; 60 | 61 | var sut = new StateTransitionAutoFallback( 62 | GetStateMachine() 63 | , startState 64 | , toState: endState 65 | , condition: _ => false 66 | , triggerState: startState 67 | , name: "test" 68 | , priority: 1); 69 | 70 | sut.Execute(new ExecutionParameters(SaleEvent.Pay, sale), GetDummyResult()); 71 | 72 | Assert.Equal(startState, sale.State); 73 | } 74 | 75 | private StateTransitionResult GetDummyResult() 76 | { 77 | return new StateTransitionResult(SaleEvent.Pay 78 | , SaleState.Open 79 | , SaleState.Open 80 | , SaleState.Open 81 | , "transactionName"); 82 | } 83 | 84 | private IStateMachine GetStateMachine() 85 | { 86 | return new StateMachine(saleToUpdate => saleToUpdate.State 87 | , (saleToUpdate, newState) => saleToUpdate.State = newState); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Sync/StateTransitionDynamicParameterizedTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | using System; 13 | using Xunit; 14 | 15 | namespace NStateManager.Tests.Sync 16 | { 17 | public class StateTransitionDynamicParameterizedTests 18 | { 19 | [Fact] 20 | public void Constructor_throws_ArgumentNullException_if_StateFuncAsync_null() 21 | { 22 | Assert.Throws(() => new StateTransitionDynamicParameterized( 23 | stateAccessor: sale => sale.State 24 | , stateMutator: (sale, newState) => sale.State = newState 25 | , stateFunc: null 26 | , name: "test" 27 | , priority: 1)); 28 | } 29 | 30 | [Fact] 31 | public void Execute_transitions_based_on_StateFuncAsync() 32 | { 33 | var sut = new StateTransitionDynamicParameterized( 34 | stateAccessor: sale => sale.State 35 | , stateMutator: (sale, newState) => sale.State = newState 36 | , stateFunc: (sale, stringParam) => SaleState.Complete 37 | , name: "test" 38 | , priority: 1); 39 | 40 | var testSale = new Sale(saleId: 87) { State = SaleState.Open }; 41 | var parameters = new ExecutionParameters(SaleEvent.Pay, testSale, request: "request"); 42 | 43 | var result = sut.Execute(parameters); 44 | 45 | Assert.Equal(SaleState.Complete, result.CurrentState); 46 | Assert.Equal(SaleState.Open, result.PreviousState); 47 | } 48 | 49 | [Fact] 50 | public void Execute_treats_lack_of_transitions_as_failed_condition() 51 | { 52 | var sut = new StateTransitionDynamicParameterized( 53 | stateAccessor: sale => sale.State 54 | , stateMutator: (sale, newState) => sale.State = newState 55 | , stateFunc: (sale, stringParam) => SaleState.Open 56 | , name: "test" 57 | , priority: 1); 58 | 59 | var testSale = new Sale(saleId: 87) { State = SaleState.Open }; 60 | var parameters = new ExecutionParameters(SaleEvent.Pay, testSale, request: "request"); 61 | 62 | var result = sut.Execute(parameters); 63 | 64 | Assert.False(result.ConditionMet); 65 | Assert.False(result.WasTransitioned); 66 | Assert.Equal(SaleState.Open, testSale.State); 67 | Assert.Equal(SaleState.Open, result.CurrentState); 68 | Assert.Equal(SaleState.Open, result.PreviousState); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Sync/StateTransitionDynamicTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | using System; 13 | using Xunit; 14 | 15 | namespace NStateManager.Tests.Sync 16 | { 17 | public class StateTransitionDynamicTests 18 | { 19 | [Fact] 20 | public void Constructor_throws_ArgumentNullException_if_stateFunc_null() 21 | { 22 | Assert.Throws(() => new StateTransitionDynamic( 23 | stateAccessor: sale => sale.State 24 | , stateMutator: (sale, newState) => sale.State = newState 25 | , stateFunc: null 26 | , name: "test" 27 | , priority: 1)); 28 | } 29 | 30 | [Fact] 31 | public void Execute_transitions_based_on_stateFunc() 32 | { 33 | var sut = new StateTransitionDynamic( 34 | stateAccessor: sale => sale.State 35 | , stateMutator: (sale, newState) => sale.State = newState 36 | , stateFunc: sale => SaleState.Complete 37 | , name: "test" 38 | , priority: 1); 39 | 40 | var testSale = new Sale(saleId: 87) { State = SaleState.Open }; 41 | var parameters = new ExecutionParameters(SaleEvent.Pay, testSale); 42 | 43 | var result = sut.Execute(parameters); 44 | 45 | Assert.Equal(SaleState.Complete, testSale.State); 46 | Assert.Equal(SaleState.Complete, result.CurrentState); 47 | Assert.Equal(SaleState.Open, result.PreviousState); 48 | } 49 | 50 | [Fact] 51 | public void Execute_treats_lack_of_transitions_as_failed_condition() 52 | { 53 | var sut = new StateTransitionDynamic( 54 | stateAccessor: sale => sale.State 55 | , stateMutator: (sale, newState) => sale.State = newState 56 | , stateFunc: sale => SaleState.Open 57 | , name: "test" 58 | , priority: 1); 59 | 60 | var testSale = new Sale(saleId: 87) { State = SaleState.Open }; 61 | var parameters = new ExecutionParameters(SaleEvent.Pay, testSale); 62 | 63 | var result = sut.Execute(parameters); 64 | 65 | Assert.False(result.ConditionMet); 66 | Assert.False(result.WasTransitioned); 67 | Assert.False(result.WasCancelled); 68 | Assert.Equal(SaleState.Open, testSale.State); 69 | Assert.Equal(SaleState.Open, result.CurrentState); 70 | Assert.Equal(SaleState.Open, result.PreviousState); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/Sync/SubStateTest.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using NStateManager.Sync; 12 | using Xunit; 13 | using Xunit.Abstractions; 14 | 15 | namespace NStateManager.Tests 16 | { 17 | public class Program 18 | { 19 | private readonly ITestOutputHelper _testOutputHelper; 20 | 21 | public Program(ITestOutputHelper testOutputHelper) 22 | { 23 | _testOutputHelper = testOutputHelper; 24 | } 25 | 26 | private class TestEntity 27 | { public State State { get; set; } } 28 | 29 | private enum State { SuperState, SubState } 30 | private enum Trigger { One, Two } 31 | 32 | [Fact] 33 | public void Test() 34 | { 35 | IStateMachine stateMachine = new StateMachine( 36 | stateAccessor: entity => entity.State 37 | , stateMutator: (entity, newState) => entity.State = newState); 38 | 39 | stateMachine.ConfigureState(State.SuperState) 40 | .AddTransition(Trigger.One, State.SubState) 41 | .AddEntryAction(_ => _testOutputHelper.WriteLine("SuperState OnEntry")) 42 | .AddExitAction(_ => _testOutputHelper.WriteLine("SuperState OnExit")); 43 | 44 | stateMachine.ConfigureState(State.SubState) 45 | .MakeSubStateOf(stateMachine.ConfigureState(State.SuperState)) 46 | .AddTransition(Trigger.Two, State.SuperState) 47 | .AddEntryAction(_ => _testOutputHelper.WriteLine("SubState OnEntry")) 48 | .AddExitAction(_ => _testOutputHelper.WriteLine("SubState OnExit")); 49 | 50 | stateMachine.OnTransition += (o, args) => _testOutputHelper.WriteLine($"{args.TransitionResult.StartingState} -[{args.TransitionResult.Trigger}]-> {args.TransitionResult.CurrentState}"); 51 | 52 | var testEntity = new TestEntity(); 53 | stateMachine.FireTrigger(testEntity, Trigger.One); 54 | stateMachine.FireTrigger(testEntity, Trigger.Two); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/TestChange.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | namespace NStateManager.Tests 13 | { 14 | public class TestChange 15 | { 16 | public TestChange(double amount) 17 | { 18 | Amount = amount; 19 | } 20 | 21 | public double Amount { get; } 22 | } 23 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/TestPayment.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | namespace NStateManager.Tests 13 | { 14 | public class TestPayment 15 | { 16 | public TestPayment(double amount) 17 | { 18 | Amount = amount; 19 | } 20 | 21 | public double Amount { get; } 22 | } 23 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/TestProduct.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | namespace NStateManager.Tests 13 | { 14 | public class TestProduct 15 | { 16 | public TestProduct(int id, string name, string singularUnitName, string pluralUnitName, double unitPrice, string imageClassName) 17 | { 18 | Id = id; 19 | Name = name; 20 | SingularUnitName = singularUnitName; 21 | PluralUnitName = pluralUnitName; 22 | UnitPrice = unitPrice; 23 | ImageClassName = imageClassName; 24 | } 25 | 26 | public int Id { get; } 27 | public string Name { get; } 28 | public string SingularUnitName { get; } 29 | public string PluralUnitName { get; } 30 | public double UnitPrice { get; } 31 | public string ImageClassName { get; } 32 | } 33 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/TestSale.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | using System; 12 | using System.Collections.Generic; 13 | using System.Linq; 14 | 15 | namespace NStateManager.Tests 16 | { 17 | public class TestSale 18 | { 19 | private readonly List _payments = new List(); 20 | private readonly List _saleItems = new List(); 21 | 22 | public TestSale(int id) 23 | { 24 | Id = id; 25 | } 26 | 27 | public double AmountPaid { get; private set; } 28 | 29 | public double Balance { get; private set; } 30 | 31 | public double ChangeGiven { get; private set; } 32 | 33 | public int Id { get; } 34 | 35 | public bool IsPaid => Math.Abs(Balance) < .001 && _payments.Any() && SaleItems.Any(i => i.Quantity > 0); 36 | 37 | public double PaymentBalance => AmountPaid - ChangeGiven; 38 | 39 | public TestSaleState State { get; internal set; } 40 | 41 | public IReadOnlyList SaleItems => _saleItems.AsReadOnly(); 42 | 43 | internal bool HasNegativeBalance() 44 | { 45 | return Balance < 0; 46 | } 47 | 48 | internal bool HasPositiveBalance() 49 | { 50 | return Balance > 0; 51 | } 52 | 53 | internal bool IsCancellable() 54 | { 55 | return Math.Abs(PaymentBalance) < 0.001; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/TestSaleEvent.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | namespace NStateManager.Tests 13 | { 14 | public enum TestSaleEvent 15 | { 16 | AddItem, 17 | SetItemQuantity, 18 | DeleteItem, 19 | Pay, 20 | GiveChange, 21 | Cancel 22 | } 23 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/TestSaleItem.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | namespace NStateManager.Tests 13 | { 14 | public class TestSaleItem 15 | { 16 | public TestSaleItem(TestProduct product, int quantity) 17 | { 18 | Product = product; 19 | Quantity = quantity; 20 | TotalPrice = quantity * product.UnitPrice; 21 | } 22 | 23 | public TestProduct Product { get; } 24 | 25 | public int Quantity { get; } 26 | 27 | public double TotalPrice { get; } 28 | } 29 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/TestSaleState.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | namespace NStateManager.Tests 13 | { 14 | public enum TestSaleState 15 | { 16 | Open, 17 | Overpaid, 18 | Finalized, 19 | Cancelled, 20 | Paid 21 | } 22 | } -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/TriggerActionParameterizedTests.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) 2018 Scott L. Carter 2 | // 3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 4 | //compliance with the License. You may obtain a copy of the License at 5 | //http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is 8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | //See the License for the specific language governing permissions and limitations under the License. 10 | #endregion 11 | 12 | using System; 13 | using Xunit; 14 | 15 | namespace NStateManager.Tests 16 | { 17 | public class TriggerActionParameterizedTests 18 | { 19 | [Fact] 20 | public void Constructor_sets_Action() 21 | { 22 | void UpdateBalance(Sale sale, double newBalance) => sale.Balance = newBalance; 23 | var sut = new TriggerActionParameterized(UpdateBalance); 24 | 25 | Assert.Equal(UpdateBalance, sut.Action); 26 | } 27 | 28 | [Fact] 29 | public void Constructor_throws_ArgumentNullException_if_Action_null() 30 | { 31 | Assert.Throws(() => new TriggerActionParameterized(action: null)); 32 | } 33 | 34 | [Fact] 35 | public void Execute_throws_ArgumentNullException_if_parameters_null() 36 | { 37 | void UpdateBalance(Sale sale, double newBalance) => sale.Balance = newBalance; 38 | var sut = new TriggerActionParameterized(UpdateBalance); 39 | 40 | Assert.Throws(() => sut.Execute(parameters: null)); 41 | } 42 | 43 | [Fact] 44 | public void Execute_throws_ArgumentException_if_parametersRequest_wrongType() 45 | { 46 | void UpdateBalance(Sale sale, double newBalance) => sale.Balance = newBalance; 47 | var sut = new TriggerActionParameterized(UpdateBalance); 48 | var parameters = new ExecutionParameters(SaleEvent.AddItem, new Sale(saleId: 24), request: "wrongType"); 49 | 50 | Assert.Throws(() => sut.Execute(parameters)); 51 | } 52 | 53 | [Fact] 54 | public void Execute_executes_Action() 55 | { 56 | void UpdateBalance(Sale sale, double newBalance) => sale.Balance = newBalance; 57 | var sut = new TriggerActionParameterized(UpdateBalance); 58 | 59 | var testSale = new Sale(saleId: 24); 60 | var updatedBalance = 23.45; 61 | var parameters = new ExecutionParameters(SaleEvent.AddItem, testSale, request: updatedBalance); 62 | 63 | sut.Execute(parameters); 64 | 65 | Assert.Equal(updatedBalance, testSale.Balance); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Tests/NStateManager.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: Visual Studio 2017 3 | before_build: 4 | - ps: dotnet restore Source\NStateManager.sln 5 | pull_requests: 6 | do_not_increment_build_number: true 7 | build: 8 | project: 'Source\NStateManager.sln' 9 | publish_nuget: false 10 | verbosity: minimal 11 | artifacts: 12 | - path: Package\**\*.nupkg 13 | skip_commits: 14 | files: 15 | - .gitattributes 16 | - .gitignore 17 | - README.md 18 | - build.bat 19 | - NStateManager.sln.licenseheader 20 | 21 | skip_tags: true --------------------------------------------------------------------------------