Compare commits
50 commits
Author | SHA1 | Date | |
---|---|---|---|
D. Moonfire | 785b651951 | ||
D. Moonfire | f6bd3cb41d | ||
D. Moonfire | bab8ae4eb8 | ||
D. Moonfire | 1d6dff4c92 | ||
D. Moonfire | 16880cb36e | ||
D. Moonfire | aa1eae40e0 | ||
D. Moonfire | 152428b235 | ||
D. Moonfire | 962e1f0d11 | ||
D. Moonfire | 0603603be0 | ||
D. Moonfire | 294d9c0a38 | ||
D. Moonfire | 4093d9c0cb | ||
D. Moonfire | f3b18dc54a | ||
70d6f57d7c | |||
8c09c36e88 | |||
1c375c45cc | |||
0ce47dd8d1 | |||
7bc5aacc4b | |||
7a9b77b4ad | |||
7411053595 | |||
52d03106bc | |||
4ce4fe26cb | |||
7c7734b03b | |||
6957dc43c5 | |||
5c66619e12 | |||
295321da0a | |||
ba538c0d86 | |||
d000882f96 | |||
7594b73846 | |||
677b12f57c | |||
619d583446 | |||
0e424fce33 | |||
78109d3890 | |||
b364f299c6 | |||
d8fa3029a9 | |||
6f6a2f9bd3 | |||
c58df120b8 | |||
ef09e49f4c | |||
b2a143d1aa | |||
276332f4c0 | |||
6ae8151b1f | |||
d49e07a94f | |||
9347397e13 | |||
caaefa3aa2 | |||
fff4493fa7 | |||
a161673e36 | |||
678bfc8564 | |||
1d6d7d4ec3 | |||
28898dbaff | |||
54d05c8485 | |||
8ef93d81a9 |
18
.config/dotnet-tools.json
Normal file
18
.config/dotnet-tools.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"gitversion.tool": {
|
||||
"version": "5.9.0",
|
||||
"commands": [
|
||||
"dotnet-gitversion"
|
||||
]
|
||||
},
|
||||
"dotnet-reportgenerator-globaltool": {
|
||||
"version": "5.1.3",
|
||||
"commands": [
|
||||
"reportgenerator"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
123
.editorconfig
123
.editorconfig
|
@ -1,27 +1,122 @@
|
|||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_brace_style = K&R
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 120
|
||||
tab_width = 4
|
||||
trim_trailing_whitespace = true
|
||||
curly_bracket_next_line = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{js,ts}]
|
||||
quote_type = double
|
||||
# Microsoft .NET properties
|
||||
csharp_new_line_before_members_in_object_initializers = false
|
||||
csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
||||
csharp_space_after_cast = false
|
||||
csharp_style_var_elsewhere = false:hint
|
||||
csharp_style_var_for_built_in_types = false:hint
|
||||
csharp_style_var_when_type_is_apparent = true:hint
|
||||
csharp_preserve_single_line_statements = false
|
||||
csharp_preserve_single_line_blocks = true
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:hint
|
||||
dotnet_style_predefined_type_for_member_access = true:hint
|
||||
dotnet_style_qualification_for_event = true:hint
|
||||
dotnet_style_qualification_for_field = true:hint
|
||||
dotnet_style_qualification_for_method = true:hint
|
||||
dotnet_style_qualification_for_property = true:hint
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:hint
|
||||
|
||||
[*.json]
|
||||
# ReSharper properties
|
||||
resharper_alignment_tab_fill_style = optimal_fill
|
||||
resharper_apply_on_completion = true
|
||||
resharper_blank_lines_after_control_transfer_statements = 1
|
||||
resharper_blank_lines_around_single_line_auto_property = 1
|
||||
resharper_blank_lines_around_single_line_property = 1
|
||||
resharper_blank_lines_before_single_line_comment = 1
|
||||
resharper_blank_lines_between_using_groups = 1
|
||||
resharper_braces_for_for = required
|
||||
resharper_braces_for_foreach = required
|
||||
resharper_braces_for_ifelse = required
|
||||
resharper_braces_for_while = required
|
||||
resharper_can_use_global_alias = false
|
||||
resharper_csharp_blank_lines_around_single_line_field = 1
|
||||
resharper_csharp_blank_lines_around_single_line_invocable = 1
|
||||
resharper_csharp_indent_style = tab
|
||||
resharper_csharp_insert_final_newline = true
|
||||
resharper_csharp_keep_blank_lines_in_code = 1
|
||||
resharper_csharp_keep_blank_lines_in_declarations = 1
|
||||
resharper_csharp_new_line_before_while = true
|
||||
resharper_csharp_use_indent_from_vs = false
|
||||
resharper_csharp_wrap_arguments_style = chop_if_long
|
||||
resharper_csharp_wrap_extends_list_style = chop_if_long
|
||||
resharper_csharp_wrap_parameters_style = chop_if_long
|
||||
resharper_css_insert_final_newline = false
|
||||
resharper_enforce_line_ending_style = true
|
||||
resharper_html_insert_final_newline = false
|
||||
resharper_indent_nested_fixed_stmt = true
|
||||
resharper_js_indent_style = tab
|
||||
resharper_js_insert_final_newline = true
|
||||
resharper_js_keep_blank_lines_in_code = 1
|
||||
resharper_js_stick_comment = false
|
||||
resharper_js_use_indent_from_vs = false
|
||||
resharper_js_wrap_before_binary_opsign = true
|
||||
resharper_js_wrap_chained_method_calls = chop_if_long
|
||||
resharper_keep_blank_lines_between_declarations = 1
|
||||
resharper_min_blank_lines_after_imports = 1
|
||||
resharper_place_attribute_on_same_line = False
|
||||
resharper_place_constructor_initializer_on_same_line = false
|
||||
resharper_place_type_constraints_on_same_line = false
|
||||
resharper_protobuf_insert_final_newline = false
|
||||
resharper_qualified_using_at_nested_scope = true
|
||||
resharper_resx_insert_final_newline = false
|
||||
resharper_space_within_single_line_array_initializer_braces = true
|
||||
resharper_use_indents_from_main_language_in_file = false
|
||||
resharper_vb_insert_final_newline = false
|
||||
resharper_wrap_after_declaration_lpar = true
|
||||
resharper_wrap_after_invocation_lpar = true
|
||||
resharper_wrap_before_extends_colon = true
|
||||
resharper_wrap_before_first_type_parameter_constraint = true
|
||||
resharper_wrap_before_type_parameter_langle = true
|
||||
resharper_xmldoc_indent_child_elements = ZeroIndent
|
||||
resharper_xmldoc_indent_text = ZeroIndent
|
||||
resharper_xmldoc_insert_final_newline = false
|
||||
resharper_xml_insert_final_newline = false
|
||||
|
||||
# ReSharper inspection severities
|
||||
resharper_check_namespace_highlighting = none
|
||||
resharper_convert_to_auto_property_highlighting = none
|
||||
resharper_localizable_element_highlighting = none
|
||||
resharper_redundant_comma_in_attribute_list_highlighting = none
|
||||
resharper_redundant_comma_in_enum_declaration_highlighting = none
|
||||
resharper_redundant_comma_in_initializer_highlighting = none
|
||||
resharper_string_compare_to_is_culture_specific_highlighting = none
|
||||
resharper_string_index_of_is_culture_specific_1_highlighting = none
|
||||
resharper_use_null_propagation_highlighting = none
|
||||
resharper_use_object_or_collection_initializer_highlighting = hint
|
||||
resharper_use_string_interpolation_highlighting = hint
|
||||
|
||||
# Matches the exact files either package.json or .travis.yml
|
||||
[{package.json,.travis.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
indent_style = space
|
||||
|
||||
[*.yaml]
|
||||
[*.{cs,js,json,jsx,proto,resjson,ts,tsx}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
|
||||
[*.{asax,ascx,aspx,cshtml,css,htm,html,master,razor,skin,vb,xaml,xamlx,xoml}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
|
||||
[*.{appxmanifest,build,config,csproj,dbml,discomap,dtd,jsproj,lsproj,njsproj,nuspec,proj,props,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
|
||||
[*.proto]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
|
|
16
.gitignore
vendored
16
.gitignore
vendored
|
@ -2,8 +2,7 @@ launchSettings.json
|
|||
|
||||
*~
|
||||
*.user
|
||||
|
||||
NaNoGenMo.*
|
||||
Directory.Build.props
|
||||
|
||||
obj/
|
||||
[Bb]in/
|
||||
|
@ -11,5 +10,16 @@ obj/
|
|||
.vscode/
|
||||
.idea/
|
||||
_ReSharper.Caches/
|
||||
node_modules/
|
||||
|
||||
.author-intrusion
|
||||
# NixOS
|
||||
.direnv/
|
||||
|
||||
# Tests and Coverage
|
||||
coverage
|
||||
TestResults/
|
||||
tests/artifacts/
|
||||
|
||||
# Lefthook
|
||||
.lefthook-local/
|
||||
lefthook-local.yml
|
||||
|
|
8
.prettierignore
Normal file
8
.prettierignore
Normal file
|
@ -0,0 +1,8 @@
|
|||
*~
|
||||
flake.*
|
||||
|
||||
node_modules/
|
||||
.direnv/
|
||||
.config/
|
||||
obj/
|
||||
bin/
|
3
.tool-versions
Normal file
3
.tool-versions
Normal file
|
@ -0,0 +1,3 @@
|
|||
dotnet-core 5.0.100
|
||||
yarn 1.22.10
|
||||
nodejs 15.0.1
|
36
.woodpecker.yml
Normal file
36
.woodpecker.yml
Normal file
|
@ -0,0 +1,36 @@
|
|||
clone:
|
||||
git:
|
||||
image: woodpeckerci/plugin-git
|
||||
settings:
|
||||
tags: true
|
||||
|
||||
pipeline:
|
||||
build:
|
||||
image: registry.gitlab.com/dmoonfire/nix-flake-docker:latest
|
||||
commands:
|
||||
- nix develop --command scripts/build.sh
|
||||
when:
|
||||
event: [push, pull_request, tag]
|
||||
tag: v*
|
||||
|
||||
test:
|
||||
image: registry.gitlab.com/dmoonfire/nix-flake-docker:latest
|
||||
commands:
|
||||
- nix develop --command scripts/test.sh
|
||||
when:
|
||||
event: [push, pull_request]
|
||||
#paths:
|
||||
# - ./**/*test-result.xml
|
||||
# - ./coverage/Cobertura.xml
|
||||
# - ./coverage/Summary.*
|
||||
# - ./**/*.nupkg
|
||||
|
||||
release-main:
|
||||
image: registry.gitlab.com/dmoonfire/nix-flake-docker:latest
|
||||
commands:
|
||||
- nix develop --command scripts/release.sh
|
||||
secrets:
|
||||
- gitea_token
|
||||
when:
|
||||
event: push
|
||||
branch: main
|
132
CODE-OF-CONDUCT.md
Normal file
132
CODE-OF-CONDUCT.md
Normal file
|
@ -0,0 +1,132 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[INSERT CONTACT METHOD].
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][mozilla coc].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][faq]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[mozilla coc]: https://github.com/mozilla/diversity
|
||||
[faq]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26124.0
|
||||
|
@ -7,7 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EE3B4148-AC0
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MfGames.Locking", "src\MfGames.Locking\MfGames.Locking.csproj", "{016D2590-46B8-4776-A7F9-1B0586920932}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MfGames.Locking.Tests", "src\MfGames.Locking.Tests\MfGames.Locking.Tests.csproj", "{50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MfGames.Locking.Tests", "tests\MfGames.Locking.Tests\MfGames.Locking.Tests.csproj", "{50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeEditing/GenerateMemberBody/DocumentationGenerationKind/@EntryValue">Inherit</s:String>
|
||||
|
||||
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CheckNamespace/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
|
@ -53,10 +54,11 @@
|
|||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ThisQualifier/INSTANCE_MEMBERS_QUALIFY_MEMBERS/@EntryValue">15</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGNMENT_TAB_FILL_STYLE/@EntryValue">OPTIMAL_FILL</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_LINQ_QUERY/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_BINARY_EXPRESSIONS_CHAIN/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_EXPRESSION/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_FOR_STMT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_PARAMETER/@EntryValue">False</s:Boolean>
|
||||
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTIPLE_DECLARATION/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_CONSTRAINS/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_LIST/@EntryValue">False</s:Boolean>
|
||||
|
@ -68,7 +70,7 @@
|
|||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_SINGLE_LINE_PROPERTY/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BEFORE_SINGLE_LINE_COMMENT/@EntryValue">1</s:Int64>
|
||||
|
||||
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/CASE_BLOCK_BRACES/@EntryValue">NEXT_LINE</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_ATTRIBUTE_STYLE/@EntryValue">SEPARATE</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_FIXED_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
|
||||
|
@ -82,7 +84,9 @@
|
|||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INITIALIZER_BRACES/@EntryValue">NEXT_LINE</s:String>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_DECLARATION_PARENS_ARRANGEMENT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/NESTED_TERNARY_STYLE/@EntryValue">EXPANDED</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_CONSTRUCTOR_INITIALIZER_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
|
@ -93,18 +97,24 @@
|
|||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_WHILE_ON_NEW_LINE/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AROUND_MULTIPLICATIVE_OP/@EntryValue">True</s:Boolean>
|
||||
|
||||
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_BEFORE_TYPEOF_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/STICK_COMMENT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/USE_INDENT_FROM_VS/@EntryValue">False</s:Boolean>
|
||||
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_DECLARATION_LPAR/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_INVOCATION_LPAR/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARGUMENTS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARRAY_INITIALIZER_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_BINARY_OPSIGN/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_EXTENDS_COLON/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_FIRST_TYPE_PARAMETER_CONSTRAINT/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_TYPE_PARAMETER_LANGLE/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_CHAINED_BINARY_EXPRESSIONS/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_CHAINED_METHOD_CALLS/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_EXTENDS_LIST_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">80</s:Int64>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_PARAMETERS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CssFormatter/ALIGNMENT_TAB_FILL_STYLE/@EntryValue">OPTIMAL_FILL</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/HtmlFormatter/ALIGNMENT_TAB_FILL_STYLE/@EntryValue">OPTIMAL_FILL</s:String>
|
||||
|
@ -128,6 +138,7 @@
|
|||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/ALIGNMENT_TAB_FILL_STYLE/@EntryValue">OPTIMAL_FILL</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/IndentSubtags/@EntryValue">ZeroIndent</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/IndentTagContent/@EntryValue">ZeroIndent</s:String>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/WRAP_LIMIT/@EntryValue">80</s:Int64>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlFormatter/ALIGNMENT_TAB_FILL_STYLE/@EntryValue">OPTIMAL_FILL</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue"><?xml version="1.0" encoding="utf-16"?>
|
||||
<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
|
||||
|
@ -521,17 +532,14 @@ II.2.12 <HandlesEvent />
|
|||
</Patterns>
|
||||
</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpMemberOrderPattern/LayoutType/@EntryValue">CustomLayout</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/AddImportsToDeepestScope/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/AddImportsToDeepestScope/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/CanUseGlobalAlias/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/QualifiedUsingAtNestedScope/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseExplicitType</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseVarWhenEvident</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseVarWhenEvident</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/EditorConfig/EnableStyleCopSupport/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue"><copyright file="$FILENAME$" company="Moonfire Games">
|
||||
Copyright (c) Moonfire Games. MIT License.
|
||||
</copyright>
|
||||
</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue"></s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/LiveTemplatesUseVar/UseVar/@EntryValue">False</s:Boolean>
|
||||
|
||||
|
||||
|
@ -660,6 +668,7 @@ Copyright (c) Moonfire Games. MIT License.
|
|||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
|
@ -669,11 +678,11 @@ Copyright (c) Moonfire Games. MIT License.
|
|||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsCodeFormatterSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsParsFormattingSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsWrapperSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EVB_002ECodeStyle_002ESettingsUpgrade_002EVBSpaceAfterUnaryMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/UnitTesting/DisabledProviders/=MSTest/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/NUnitProvider/UseNUnit3/@EntryValue">Always</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/UnitTesting/ShadowCopy/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue"><data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="MfGames.Locking.Tests" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data></s:String>
|
||||
<s:String x:Key="/Default/Housekeeping/Layout/SolBuilderDuoView/ShowBuildResultsMode/@EntryValue">ForWarningsOrErrors</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/QuickList/=32F5B2BD4CFE0E42ADE73B7FB68EBDD0/Entry/=4C84A7D189E28A43AA2631BBE4694112/EntryName/@EntryValue">tag</s:String>
|
||||
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/QuickList/=32F5B2BD4CFE0E42ADE73B7FB68EBDD0/Entry/=4C84A7D189E28A43AA2631BBE4694112/Position/@EntryValue">10</s:Int64>
|
||||
|
@ -969,6 +978,24 @@ $END$
|
|||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4EE6D125A668F541B499E002F8C91EA6/Scope/=E6E678D4B937A84D8C4585DDD2F27DB0/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4EE6D125A668F541B499E002F8C91EA6/Scope/=E6E678D4B937A84D8C4585DDD2F27DB0/CustomProperties/=minimumLanguageVersion/@EntryIndexedValue">2.0</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4EE6D125A668F541B499E002F8C91EA6/Scope/=E6E678D4B937A84D8C4585DDD2F27DB0/Type/@EntryValue">InCSharpExpression</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Description/@EntryValue">Inserts a strongly-type identifier property.</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Field/=Type/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Field/=Type/Order/@EntryValue">0</s:Int64>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Field/=When/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Field/=When/Expression/@EntryValue">getCurrentTime("yyyy_MM_dd_HHmmss")</s:String>
|
||||
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Field/=When/InitialRange/@EntryValue">-1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Field/=When/Order/@EntryValue">1</s:Int64>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Reformat/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Scope/=C3001E7C0DA78E4487072B7E050D86C5/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Scope/=C3001E7C0DA78E4487072B7E050D86C5/CustomProperties/=minimumLanguageVersion/@EntryIndexedValue">2.0</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Scope/=C3001E7C0DA78E4487072B7E050D86C5/Type/@EntryValue">InCSharpFile</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Shortcut/@EntryValue">InsertIdProperty</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/ShortenQualifiedReferences/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F3AD8F5DF3C364484232DF4AF67AA51/Text/@EntryValue"> /// <inheritdoc />
|
||||
public override $Type$Id $Type$Id => new $Type$Id($When$);
|
||||
</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=545048523B7C7941957D94940296431C/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=545048523B7C7941957D94940296431C/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=545048523B7C7941957D94940296431C/Categories/=Imported_00201_002F13_002F2012/@EntryIndexedValue">Imported 1/13/2012</s:String>
|
||||
|
@ -1001,6 +1028,26 @@ $END$</s:String>
|
|||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=545048523B7C7941957D94940296431C/Scope/=2C285F182AC98D44B0B4F29D4D2149EC/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=545048523B7C7941957D94940296431C/Scope/=2C285F182AC98D44B0B4F29D4D2149EC/CustomProperties/=minimumLanguageVersion/@EntryIndexedValue">2.0</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=545048523B7C7941957D94940296431C/Scope/=2C285F182AC98D44B0B4F29D4D2149EC/Type/@EntryValue">InCSharpStatement</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Description/@EntryValue">Inserts a strongly-typed identifier.</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Field/=Type/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Field/=Type/Order/@EntryValue">0</s:Int64>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Field/=v/@KeyIndexDefined">False</s:Boolean>
|
||||
|
||||
|
||||
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Field/=When/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Field/=When/Expression/@EntryValue">getCurrentTime("yyyy_MM_dd_HHmmss")</s:String>
|
||||
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Field/=When/InitialRange/@EntryValue">-1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Field/=When/Order/@EntryValue">1</s:Int64>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Reformat/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Scope/=C3001E7C0DA78E4487072B7E050D86C5/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Scope/=C3001E7C0DA78E4487072B7E050D86C5/CustomProperties/=minimumLanguageVersion/@EntryIndexedValue">2.0</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Scope/=C3001E7C0DA78E4487072B7E050D86C5/Type/@EntryValue">InCSharpFile</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Shortcut/@EntryValue">InsertId</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/ShortenQualifiedReferences/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=67C8134CFAF35D4C808C6D875D9C5664/Text/@EntryValue">new $Type$Id($When$)</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=77A68208F358434497E992158D06197B/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=77A68208F358434497E992158D06197B/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=77A68208F358434497E992158D06197B/Description/@EntryValue">Calls the session server to validate a user</s:String>
|
||||
|
@ -1180,23 +1227,36 @@ if ($CollectionType$Collection.Count > 0)
|
|||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=B32FEF31AA09414AB47F1EEA3625907F/Scope/=2C285F182AC98D44B0B4F29D4D2149EC/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=B32FEF31AA09414AB47F1EEA3625907F/Scope/=2C285F182AC98D44B0B4F29D4D2149EC/CustomProperties/=minimumLanguageVersion/@EntryIndexedValue">2.0</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=B32FEF31AA09414AB47F1EEA3625907F/Scope/=2C285F182AC98D44B0B4F29D4D2149EC/Type/@EntryValue">InCSharpStatement</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Applicability/=Surround/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Categories/=Imported_00201_002F13_002F2012/@EntryIndexedValue">Imported 1/13/2012</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Categories/=WEI_0020Standard/@EntryIndexedValue">WEI Standard</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Description/@EntryValue">#region (WEI)</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Reformat/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Shortcut/@EntryValue">#region(WEI)</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/ShortenQualifiedReferences/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Text/@EntryValue">#region $RegionName$$END$
|
||||
$SELECTION$
|
||||
#endregion $RegionName$
|
||||
</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Field/=RegionName/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Field/=RegionName/Order/@EntryValue">0</s:Int64>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Scope/=C3001E7C0DA78E4487072B7E050D86C5/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Scope/=C3001E7C0DA78E4487072B7E050D86C5/CustomProperties/=minimumLanguageVersion/@EntryIndexedValue">2.0</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/Scope/=C3001E7C0DA78E4487072B7E050D86C5/Type/@EntryValue">InCSharpFile</s:String>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CAB80A3A33B30B40952EEB95DF15BC47/@KeyIndexDefined">False</s:Boolean>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=CB679803BBC2764A9BE443499424A817/@KeyIndexDefined">False</s:Boolean>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=D4AA4DA08F1F4444BC18D5C7993DF382/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=D4AA4DA08F1F4444BC18D5C7993DF382/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=D4AA4DA08F1F4444BC18D5C7993DF382/Categories/=Imported_00201_002F13_002F2012/@EntryIndexedValue">Imported 1/13/2012</s:String>
|
||||
|
@ -1304,4 +1364,6 @@ using(DataAccessAdapter dataAccessAdapter = new DataAccessAdapter(ConnectionStri
|
|||
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=F87CBA43E9CDCC41A45B39A2A2A25764/Field/=EntityType/Order/@EntryValue">0</s:Int64>
|
||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=F87CBA43E9CDCC41A45B39A2A2A25764/Scope/=2C285F182AC98D44B0B4F29D4D2149EC/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=F87CBA43E9CDCC41A45B39A2A2A25764/Scope/=2C285F182AC98D44B0B4F29D4D2149EC/CustomProperties/=minimumLanguageVersion/@EntryIndexedValue">2.0</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=F87CBA43E9CDCC41A45B39A2A2A25764/Scope/=2C285F182AC98D44B0B4F29D4D2149EC/Type/@EntryValue">InCSharpStatement</s:String></wpf:ResourceDictionary>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=F87CBA43E9CDCC41A45B39A2A2A25764/Scope/=2C285F182AC98D44B0B4F29D4D2149EC/Type/@EntryValue">InCSharpStatement</s:String>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Tocks/@EntryIndexedValue">True</s:Boolean>
|
||||
</wpf:ResourceDictionary>
|
||||
|
|
16
NuGet.Config
Normal file
16
NuGet.Config
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
<add key="mfgames.com" value="https://src.mfgames.com/api/packages/mfgames-cil/nuget/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
<packageSourceMapping>
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
<packageSource key="mfgames.com">
|
||||
<package pattern="MfGames.*" />
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
</configuration>
|
42
flake.lock
Normal file
42
flake.lock
Normal file
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1659877975,
|
||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1662019588,
|
||||
"narHash": "sha256-oPEjHKGGVbBXqwwL+UjsveJzghWiWV0n9ogo1X6l4cw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2da64a81275b68fdad38af669afeda43d401e94b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-unstable",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
24
flake.nix
Normal file
24
flake.nix
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
description = "A .NET core library for easily building CLI tools";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let pkgs = nixpkgs.legacyPackages.${system};
|
||||
in {
|
||||
devShell = pkgs.mkShell {
|
||||
buildInputs = [
|
||||
pkgs.dotnet-sdk
|
||||
pkgs.lefthook
|
||||
pkgs.convco
|
||||
pkgs.nodePackages.prettier
|
||||
pkgs.nixfmt
|
||||
pkgs.jq
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
22
lefthook.yml
Normal file
22
lefthook.yml
Normal file
|
@ -0,0 +1,22 @@
|
|||
pre-commit:
|
||||
parallel: true
|
||||
commands:
|
||||
dotnet-format:
|
||||
glob: "*.cs"
|
||||
run: dotnet format
|
||||
prettier:
|
||||
run: prettier . --write --loglevel warn
|
||||
nixfmt:
|
||||
run: nixfmt flake.nix
|
||||
|
||||
commit-msg:
|
||||
commands:
|
||||
commit-check:
|
||||
run: convco check -n 1
|
||||
|
||||
skip_output:
|
||||
- meta # Skips lefthook version printing
|
||||
- summary # Skips summary block (successful and failed steps) printing
|
||||
- success # Skips successful steps printing
|
||||
- failure # Skips failed steps printing
|
||||
- execution # Skips printing successfully executed commands and their output (but still prints failed executions)
|
23
scripts/README.md
Normal file
23
scripts/README.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Scripts Directory
|
||||
|
||||
This directory contains the basic scripts for working with the library.
|
||||
|
||||
## `setup.sh`
|
||||
|
||||
This verifies the environment is correct and makes sure everything is configured.
|
||||
|
||||
## `build.sh`
|
||||
|
||||
This builds the project and creates the binaries in debug mode.
|
||||
|
||||
## `test.sh`
|
||||
|
||||
This runs any required tests.
|
||||
|
||||
## `format.sh`
|
||||
|
||||
This is used to format the code base using our standards. It matches the commands in the `lefthook` pre-commit hook.
|
||||
|
||||
## `release.sh`
|
||||
|
||||
Intended to run in a CI environment, this creates a NuGet package and publishes it.
|
7
scripts/build.sh
Executable file
7
scripts/build.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
cd $(dirname $0)/..
|
||||
./scripts/setup.sh || exit 1
|
||||
|
||||
echo "$(basename $0): building project"
|
||||
dotnet build
|
4
scripts/format.sh
Executable file
4
scripts/format.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
cd $(dirname $0)/..
|
||||
lefthook run pre-commit
|
51
scripts/release.sh
Executable file
51
scripts/release.sh
Executable file
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
cd $(dirname $0)/..
|
||||
./scripts/setup.sh || exit 1
|
||||
|
||||
# Verify the input.
|
||||
if [ "x$GITEA_TOKEN" = "x" ]
|
||||
then
|
||||
echo "the environment variable GITEA_TOKEN is not defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean up everything from the previous runs.
|
||||
echo "$(basename $0): cleaning project"
|
||||
dotnet clean
|
||||
|
||||
# Version the file based on the Git repository.
|
||||
echo "$(basename $0): setting project version"
|
||||
(cd src && dotnet dotnet-gitversion /updateprojectfiles)
|
||||
SEMVER="v$(dotnet gitversion /output json | jq -r .SemVer)"
|
||||
|
||||
if [ "x$SEMVER" = "x" ]
|
||||
then
|
||||
echo "$(basename $0): cannot figure out the semantic version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build to pick up the new version.
|
||||
echo "$(basename $0): building project $SEMVER"
|
||||
dotnet build || exit 1
|
||||
|
||||
# Create and publish the NuGet packages.
|
||||
echo "$(basename $0): creating NuGet packages"
|
||||
dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg || exit 1
|
||||
|
||||
echo "$(basename $0): publishing NuGet package"
|
||||
dotnet nuget remove source mfgames.com >& /dev/null
|
||||
dotnet nuget add source --name mfgames.com --username dmoonfire --password $GITEA_TOKEN https://src.mfgames.com/api/packages/mfgames-cil/nuget/index.json --store-password-in-clear-text || exit 1
|
||||
dotnet nuget push --skip-duplicate --source mfgames.com src/*/bin/Debug/*.nupkg || exit 1
|
||||
|
||||
# Tag and push, but only if we don't have a tag.
|
||||
if ! git tag | grep $SEMVER >& /dev/null
|
||||
then
|
||||
echo "$(basename $0): tagging and pushing"
|
||||
git remote add publish https://dmoonfire:$GITEA_TOKEN@src.mfgames.com/mfgames-cil/$(basename $(git config --get remote.origin.url))
|
||||
git tag $SEMVER
|
||||
git push publish $SEMVER || exit 1
|
||||
git remote remove publish
|
||||
else
|
||||
echo "$(basename $0): not tagging, already exists"
|
||||
fi
|
28
scripts/setup.sh
Executable file
28
scripts/setup.sh
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
# Normalize our environment.
|
||||
cd $(dirname $0)/..
|
||||
|
||||
# Make sure we have the needed executables installed.
|
||||
for e in dotnet lefthook prettier nixfmt
|
||||
do
|
||||
if ! which $e >& /dev/null
|
||||
then
|
||||
echo "Cannot find '$e' in the path"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Make sure we have lefthook is installed.
|
||||
if [ ! -f .git/hooks/pre-commit ]
|
||||
then
|
||||
echo "$(basename $0): installing lefthook"
|
||||
lefthook install
|
||||
fi
|
||||
|
||||
# Make sure our tools are installed.
|
||||
echo "$(basename $0): install .NET tools"
|
||||
dotnet tool restore
|
||||
|
||||
# Everything is good.
|
||||
exit 0
|
12
scripts/test.sh
Executable file
12
scripts/test.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
if [ -f ./tests/*/*.csproj ]
|
||||
then
|
||||
./scripts/setup.sh || exit 1
|
||||
|
||||
dotnet test --test-adapter-path:. --logger:"junit;LogFilePath=../artifacts/{assembly}-test-result.xml;MethodFormat=Default;FailureBodyFormat=Verbose" --collect:"XPlat Code Coverage"
|
||||
dotnet tool run reportgenerator -reports:tests/*/TestResults/*/coverage.cobertura.xml -targetdir:./coverage "-reporttypes:Cobertura;TextSummary"
|
||||
grep "Line coverage" coverage/Summary.txt
|
||||
fi
|
4
scripts/update-template.sh
Executable file
4
scripts/update-template.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
cd $(dirname $0)/..
|
||||
git pull template main --no-rebase
|
15
src/Directory.Build.props
Normal file
15
src/Directory.Build.props
Normal file
|
@ -0,0 +1,15 @@
|
|||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>1.0.3</Version>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>Dylan Moonfire</Authors>
|
||||
<Company>Moonfire Games</Company>
|
||||
<RepositoryUrl>https://gitlab.com/mfgames-cil/mfgames-locking-cil</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<PackageTags>io</PackageTags>
|
||||
<PackageProjectUrl>https://gitlab.com/mfgames-cil/mfgames-locking-cil</PackageProjectUrl>
|
||||
<PackageLicense>MIT</PackageLicense>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -1,209 +0,0 @@
|
|||
// <copyright file="LockTests.cs" company="Moonfire Games">
|
||||
// Copyright (c) Moonfire Games. MIT License.
|
||||
// </copyright>
|
||||
|
||||
namespace MfGames.Locking.Tests
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
public class LockTests
|
||||
{
|
||||
public static readonly TimeSpan ProcessTime = TimeSpan.FromMilliseconds(50);
|
||||
|
||||
private readonly List<int> events;
|
||||
|
||||
private readonly ReaderWriterLockSlim locker;
|
||||
|
||||
private readonly ITestOutputHelper output;
|
||||
|
||||
private readonly DateTime start;
|
||||
|
||||
public LockTests(ITestOutputHelper output)
|
||||
{
|
||||
this.output = output;
|
||||
this.start = DateTime.UtcNow;
|
||||
this.locker = new ReaderWriterLockSlim();
|
||||
this.events = new List<int>();
|
||||
}
|
||||
|
||||
private TimeSpan Elapsed => DateTime.UtcNow - this.start;
|
||||
|
||||
[Fact]
|
||||
public void BasicDataGatheringWorks()
|
||||
{
|
||||
this.Report(ProcessTime, 1);
|
||||
this.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBlocksWrite()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.ReportInReadLock(ProcessTime * 0, ProcessTime * 3, 1)),
|
||||
Task.Run(() => this.ReportInWriteLock(ProcessTime * 1, ProcessTime * 1, 2)));
|
||||
|
||||
this.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadsDoNotBlockReads()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.ReportInReadLock(ProcessTime, ProcessTime * 5, 3)),
|
||||
Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime, 1)),
|
||||
Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime * 2, 2)));
|
||||
|
||||
this.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ThreadedDataGatheringWorks()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.ReportInReadLock(ProcessTime, ProcessTime, 1)));
|
||||
|
||||
this.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpgradableBlocksUpgradable()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.ReportInUpgradableLock(ProcessTime, ProcessTime * 5, 1)),
|
||||
Task.Run(() => this.ReportInUpgradableLock(ProcessTime * 3, ProcessTime, 2)),
|
||||
Task.Run(() => this.ReportInUpgradableLock(ProcessTime * 2, ProcessTime * 2, 3)));
|
||||
|
||||
this.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpgradableDoesNotBlockReads()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.ReportInUpgradableLock(ProcessTime, ProcessTime * 5, 3)),
|
||||
Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime, 1)),
|
||||
Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime * 2, 2)));
|
||||
|
||||
this.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBlockRead()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.ReportInWriteLock(ProcessTime, ProcessTime * 3, 1)),
|
||||
Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime, 2)));
|
||||
|
||||
this.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBlockReads()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.ReportInWriteLock(ProcessTime, ProcessTime * 5, 1)),
|
||||
Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime, 2)),
|
||||
Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime * 2, 3)));
|
||||
|
||||
this.Verify();
|
||||
}
|
||||
|
||||
private void AddEvent(int sequence)
|
||||
{
|
||||
// This is a monitor lock on the slim, just to give us a second lock.
|
||||
lock (this.locker)
|
||||
{
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => adding sequence");
|
||||
this.events.Add(sequence);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits a short period of time and then injects the sequence into the event list.
|
||||
/// </summary>
|
||||
private void Report(
|
||||
TimeSpan processTime,
|
||||
int sequence)
|
||||
{
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => starting process");
|
||||
Thread.Sleep(processTime);
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => finished process");
|
||||
this.AddEvent(sequence);
|
||||
}
|
||||
|
||||
private void ReportInReadLock(
|
||||
TimeSpan settleTime,
|
||||
TimeSpan processTime,
|
||||
int sequence)
|
||||
{
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => entering read lock: " + sequence);
|
||||
|
||||
Thread.Sleep(settleTime);
|
||||
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => locking read lock: " + sequence);
|
||||
|
||||
using (new ReadLock(this.locker))
|
||||
{
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => starting read lock: " + sequence);
|
||||
|
||||
this.Report(processTime, sequence);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReportInUpgradableLock(
|
||||
TimeSpan settleTime,
|
||||
TimeSpan processTime,
|
||||
int sequence)
|
||||
{
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => entering upgradable lock: " + sequence);
|
||||
|
||||
Thread.Sleep(settleTime);
|
||||
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => locking upgradable lock: " + sequence);
|
||||
|
||||
using (new UpgradableLock(this.locker))
|
||||
{
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => starting upgradable lock: " + sequence);
|
||||
|
||||
this.Report(processTime, sequence);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReportInWriteLock(
|
||||
TimeSpan settleTime,
|
||||
TimeSpan processTime,
|
||||
int sequence)
|
||||
{
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => entering write lock: " + sequence);
|
||||
|
||||
Thread.Sleep(settleTime);
|
||||
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => locking write lock: " + sequence);
|
||||
|
||||
using (new WriteLock(this.locker))
|
||||
{
|
||||
this.output.WriteLine(this.Elapsed + ": " + sequence + " => starting write lock: " + sequence);
|
||||
|
||||
this.Report(processTime, sequence);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the sequence of events.
|
||||
/// </summary>
|
||||
private void Verify()
|
||||
{
|
||||
this.output.WriteLine("Final Sequence: " + string.Join(", ", this.events));
|
||||
|
||||
for (int i = 1; i <= this.events.Count; i++)
|
||||
{
|
||||
Assert.Equal(i, this.events[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MfGames.Locking\MfGames.Locking.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,37 +1,18 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<Authors>Dylan Moonfire</Authors>
|
||||
<Company>Moonfire Games</Company>
|
||||
<RepositoryUrl>https://gitlab.com/dmoonfire/mfgames-locking-cil</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<PackageTags>locking</PackageTags>
|
||||
<PackageProjectUrl>https://gitlab.com/dmoonfire/mfgames-locking-cil</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://gitlab.com/dmoonfire/mfgames-locking-cil/blob/master/LICENSE.txt</PackageLicenseUrl>
|
||||
<Description>An IDisposable pattern for using ReaderWriterLockSlim.</Description>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<OutputPath>..\..\bin</OutputPath>
|
||||
<DocumentationFile>..\..\bin\$(AssemblyName).xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<OutputPath>..\..\bin</OutputPath>
|
||||
<DocumentationFile>..\..\bin\$(AssemblyName).xml</DocumentationFile>
|
||||
<TargetFrameworks>netstandard2.0;net6</TargetFrameworks>
|
||||
<Description>Wrappers and patterns for working with ReaderWriterLockSlim.</Description>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Serialization\**" />
|
||||
<EmbeddedResource Remove="Serialization\**" />
|
||||
<None Remove="Serialization\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="../stylecop.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007" PrivateAssets="All" />
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
// <copyright file="NestableReadLock.cs" company="Moonfire Games">
|
||||
// Copyright (c) Moonfire Games. MIT License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace MfGames.Locking
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a ReaderWriterLockSlim read-only lock.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
// <copyright file="NestableUpgradableReadLock.cs" company="Moonfire Games">
|
||||
// Copyright (c) Moonfire Games. MIT License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace MfGames.Locking
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a ReaderWriterLockSlim upgradable read lock.
|
||||
/// </summary>
|
||||
|
@ -17,10 +13,12 @@ namespace MfGames.Locking
|
|||
private readonly ReaderWriterLockSlim readerWriterLockSlim;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NestableUpgradableReadLock" /> class.
|
||||
/// Initializes a new instance of the <see cref="NestableUpgradableReadLock" />
|
||||
/// class.
|
||||
/// </summary>
|
||||
/// <param name="readerWriterLockSlim">The reader writer lock slim.</param>
|
||||
public NestableUpgradableReadLock(ReaderWriterLockSlim readerWriterLockSlim)
|
||||
public NestableUpgradableReadLock(
|
||||
ReaderWriterLockSlim readerWriterLockSlim)
|
||||
{
|
||||
// Keep track of the lock since we'll need it to release the lock.
|
||||
this.readerWriterLockSlim = readerWriterLockSlim;
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
// <copyright file="NestableWriteLock.cs" company="Moonfire Games">
|
||||
// Copyright (c) Moonfire Games. MIT License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace MfGames.Locking
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a ReaderWriterLockSlim write lock.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
// <copyright file="ReadLock.cs" company="Moonfire Games">
|
||||
// Copyright (c) Moonfire Games. MIT License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace MfGames.Locking
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Defines a ReaderWriterLockSlim read-only lock.
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
// <copyright file="TryGetCreate.cs" company="Moonfire Games">
|
||||
// Copyright (c) Moonfire Games. MIT License.
|
||||
// </copyright>
|
||||
|
||||
namespace MfGames.Locking
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the basic pattern for getting an item from a cache using
|
||||
/// the ReaderWriterLockSlim class. This attempts to get it using a read-only
|
||||
/// lock. If that fails, it gets an upgradable lock, tries again, and if it
|
||||
/// still can't find it, upgrades the lock to a write lock to create it.
|
||||
/// </summary>
|
||||
public static class TryGetCreate
|
||||
{
|
||||
/// <summary>
|
||||
/// Invokes the try/get/create pattern used a condition to test for it
|
||||
/// and a constructor function.
|
||||
/// </summary>
|
||||
/// <param name="readerWriterLockSlim">The reader writer lock slim.</param>
|
||||
/// <param name="conditionHandler">The condition handler.</param>
|
||||
/// <param name="createHandler">The create handler.</param>
|
||||
public static void Invoke(
|
||||
ReaderWriterLockSlim readerWriterLockSlim,
|
||||
Func<bool> conditionHandler,
|
||||
Action createHandler)
|
||||
{
|
||||
using (new ReadLock(readerWriterLockSlim))
|
||||
{
|
||||
// Verify that the condition for creating it is false.
|
||||
if (!conditionHandler())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We failed to get the lock using the read-only. We create an upgradable lock
|
||||
// and try again since it may have been created with a race condition when the
|
||||
// last lock was released and this one was acquired.
|
||||
using (new UpgradableLock(readerWriterLockSlim))
|
||||
{
|
||||
// Verify that the condition for creating it is false.
|
||||
if (!conditionHandler())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We failed to get it in the lock. Upgrade the lock to a write and create it.
|
||||
using (new WriteLock(readerWriterLockSlim))
|
||||
{
|
||||
createHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the try/get/create pattern using a tryget retrieval and a
|
||||
/// creator handler.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInput">The type of the input.</typeparam>
|
||||
/// <typeparam name="TOutput">The type of the output.</typeparam>
|
||||
/// <param name="readerWriterLockSlim">The reader writer lock slim.</param>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <param name="tryGetHandler">The try get handler.</param>
|
||||
/// <param name="createHandler">The create handler.</param>
|
||||
/// <returns>The requested output object.</returns>
|
||||
public static TOutput Invoke<TInput, TOutput>(
|
||||
ReaderWriterLockSlim readerWriterLockSlim,
|
||||
TInput input,
|
||||
TryGetHandler<TInput, TOutput> tryGetHandler,
|
||||
Func<TInput, TOutput> createHandler)
|
||||
{
|
||||
// First attempt to get the item using a read-only lock.
|
||||
TOutput output;
|
||||
|
||||
using (new ReadLock(readerWriterLockSlim))
|
||||
{
|
||||
// Try to get the item using the try/get handler.
|
||||
if (tryGetHandler(input, out output))
|
||||
{
|
||||
// We successful got the item in the read-only cache, so just return it.
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
// We failed to get the lock using the read-only. We create an upgradable lock
|
||||
// and try again since it may have been created with a race condition when the
|
||||
// last lock was released and this one was acquired.
|
||||
using (new UpgradableLock(readerWriterLockSlim))
|
||||
{
|
||||
// Try to get the item using the try/get handler.
|
||||
if (tryGetHandler(input, out output))
|
||||
{
|
||||
// We successful got the item in this lock, so return it without
|
||||
// upgrading the lock.
|
||||
return output;
|
||||
}
|
||||
|
||||
// We failed to get it in the lock. Upgrade the lock to a write and create it.
|
||||
using (new WriteLock(readerWriterLockSlim))
|
||||
{
|
||||
return createHandler(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// <copyright file="TryGetHandler.cs" company="Moonfire Games">
|
||||
// Copyright (c) Moonfire Games. MIT License.
|
||||
// </copyright>
|
||||
|
||||
namespace MfGames.Locking
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the common try/get handler to retrieve an item of a given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInput">The type of the input or lookup key.</typeparam>
|
||||
/// <typeparam name="TOutput">The type of the output.</typeparam>
|
||||
/// <param name="input">The input value to search for.</param>
|
||||
/// <param name="output">The resulting output value.</param>
|
||||
/// <returns>True if the attempt was successful, otherwise false.</returns>
|
||||
public delegate bool TryGetHandler<in TInput, TOutput>(
|
||||
TInput input,
|
||||
out TOutput output);
|
||||
}
|
|
@ -1,12 +1,8 @@
|
|||
// <copyright file="UpgradableLock.cs" company="Moonfire Games">
|
||||
// Copyright (c) Moonfire Games. MIT License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace MfGames.Locking
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a ReaderWriterLockSlim read-only lock.
|
||||
/// </summary>
|
||||
|
@ -25,7 +21,8 @@ namespace MfGames.Locking
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or
|
||||
/// resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
// <copyright file="WriteLock.cs" company="Moonfire Games">
|
||||
// Copyright (c) Moonfire Games. MIT License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace MfGames.Locking
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a ReaderWriterLockSlim read-only lock.
|
||||
/// </summary>
|
||||
|
@ -25,7 +21,8 @@ namespace MfGames.Locking
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or
|
||||
/// resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
|
|
1
src/README.md
Normal file
1
src/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Source Directories
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"$schema":
|
||||
"https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
|
||||
"settings": {
|
||||
"documentationRules": {
|
||||
"companyName": "Moonfire Games",
|
||||
"copyrightText": "Copyright (c) Moonfire Games. MIT License."
|
||||
}
|
||||
}
|
||||
}
|
14
tests/MfGames.Locking.Tests/LockAction.cs
Normal file
14
tests/MfGames.Locking.Tests/LockAction.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace MfGames.Locking.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// The various states for testing lock logic.
|
||||
/// </summary>
|
||||
internal enum LockAction
|
||||
{
|
||||
BeforeLock,
|
||||
|
||||
InLock,
|
||||
|
||||
AfterLock,
|
||||
}
|
||||
}
|
228
tests/MfGames.Locking.Tests/LockTests.cs
Normal file
228
tests/MfGames.Locking.Tests/LockTests.cs
Normal file
|
@ -0,0 +1,228 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using KellermanSoftware.CompareNetObjects;
|
||||
|
||||
using Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace MfGames.Locking.Tests
|
||||
{
|
||||
public class LockTests
|
||||
{
|
||||
private readonly ReaderWriterLockSlim locker;
|
||||
|
||||
private readonly List<(int, LockAction)> sequenceRecord;
|
||||
|
||||
private readonly object syncLock;
|
||||
|
||||
public LockTests()
|
||||
{
|
||||
this.syncLock = new object();
|
||||
this.sequenceRecord = new List<(int, LockAction)>();
|
||||
this.locker = new ReaderWriterLockSlim();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBlocksWrite()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.TestRead(1, 5, 1)),
|
||||
Task.Run(() => this.TestWrite(2, 2, 2)));
|
||||
|
||||
this.VerifySequenceRecord(
|
||||
(1, LockAction.BeforeLock),
|
||||
(1, LockAction.InLock),
|
||||
(2, LockAction.BeforeLock),
|
||||
(1, LockAction.AfterLock),
|
||||
(2, LockAction.InLock),
|
||||
(2, LockAction.AfterLock));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadsDoNotBlockReads()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.TestRead(1, 8, 1)),
|
||||
Task.Run(() => this.TestRead(2, 1, 2)),
|
||||
Task.Run(() => this.TestRead(4, 1, 3)));
|
||||
|
||||
this.VerifySequenceRecord(
|
||||
(1, LockAction.BeforeLock),
|
||||
(1, LockAction.InLock),
|
||||
(2, LockAction.BeforeLock),
|
||||
(2, LockAction.InLock),
|
||||
(2, LockAction.AfterLock),
|
||||
(3, LockAction.BeforeLock),
|
||||
(3, LockAction.InLock),
|
||||
(3, LockAction.AfterLock),
|
||||
(1, LockAction.AfterLock));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpgradableBlocksUpgradable()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.TestUpgradable(1, 10, 1)),
|
||||
Task.Run(() => this.TestUpgradable(2, 2, 2)),
|
||||
Task.Run(() => this.TestUpgradable(5, 1, 3)));
|
||||
|
||||
this.VerifySequenceRecord(
|
||||
(1, LockAction.BeforeLock),
|
||||
(1, LockAction.InLock),
|
||||
(2, LockAction.BeforeLock),
|
||||
(3, LockAction.BeforeLock),
|
||||
(1, LockAction.AfterLock),
|
||||
(2, LockAction.InLock),
|
||||
(2, LockAction.AfterLock),
|
||||
(3, LockAction.InLock),
|
||||
(3, LockAction.AfterLock));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpgradableDoesNotBlockReads()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.TestUpgradable(1, 6, 1)),
|
||||
Task.Run(() => this.TestRead(2, 1, 2)),
|
||||
Task.Run(() => this.TestRead(4, 1, 3)));
|
||||
|
||||
this.VerifySequenceRecord(
|
||||
(1, LockAction.BeforeLock),
|
||||
(1, LockAction.InLock),
|
||||
(2, LockAction.BeforeLock),
|
||||
(2, LockAction.InLock),
|
||||
(2, LockAction.AfterLock),
|
||||
(3, LockAction.BeforeLock),
|
||||
(3, LockAction.InLock),
|
||||
(3, LockAction.AfterLock),
|
||||
(1, LockAction.AfterLock));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBlockRead()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.TestWrite(1, 6, 1)),
|
||||
Task.Run(() => this.TestRead(2, 1, 2)));
|
||||
|
||||
this.VerifySequenceRecord(
|
||||
(1, LockAction.BeforeLock),
|
||||
(1, LockAction.InLock),
|
||||
(2, LockAction.BeforeLock),
|
||||
(1, LockAction.AfterLock),
|
||||
(2, LockAction.InLock),
|
||||
(2, LockAction.AfterLock));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBlockReads()
|
||||
{
|
||||
Task.WaitAll(
|
||||
Task.Run(() => this.TestWrite(1, 6, 1)),
|
||||
Task.Run(() => this.TestRead(2, 1, 2)));
|
||||
|
||||
this.VerifySequenceRecord(
|
||||
(1, LockAction.BeforeLock),
|
||||
(1, LockAction.InLock),
|
||||
(2, LockAction.BeforeLock),
|
||||
(1, LockAction.AfterLock),
|
||||
(2, LockAction.InLock),
|
||||
(2, LockAction.AfterLock));
|
||||
}
|
||||
|
||||
private void RecordSequenceAction(int sequence, LockAction action)
|
||||
{
|
||||
lock (this.syncLock)
|
||||
{
|
||||
this.sequenceRecord.Add((sequence, action));
|
||||
}
|
||||
}
|
||||
|
||||
private void Test(
|
||||
Func<ReaderWriterLockSlim, IDisposable> getLock,
|
||||
int settleTocks,
|
||||
int waitTocks,
|
||||
int sequence)
|
||||
{
|
||||
const int TocksInMilliseconds = 200;
|
||||
|
||||
Thread.Sleep(TocksInMilliseconds * settleTocks);
|
||||
|
||||
this.RecordSequenceAction(sequence, LockAction.BeforeLock);
|
||||
|
||||
using (getLock(this.locker))
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
|
||||
this.RecordSequenceAction(sequence, LockAction.InLock);
|
||||
|
||||
Thread.Sleep(TocksInMilliseconds * waitTocks);
|
||||
|
||||
this.RecordSequenceAction(sequence, LockAction.AfterLock);
|
||||
}
|
||||
}
|
||||
|
||||
private void TestRead(
|
||||
int settleTocks,
|
||||
int waitTocks,
|
||||
int sequence)
|
||||
{
|
||||
this.Test(
|
||||
x => new ReadLock(x),
|
||||
settleTocks,
|
||||
waitTocks,
|
||||
sequence);
|
||||
}
|
||||
|
||||
private void TestUpgradable(
|
||||
int settleTocks,
|
||||
int waitTocks,
|
||||
int sequence)
|
||||
{
|
||||
this.Test(
|
||||
x => new UpgradableLock(x),
|
||||
settleTocks,
|
||||
waitTocks,
|
||||
sequence);
|
||||
}
|
||||
|
||||
private void TestWrite(
|
||||
int settleTocks,
|
||||
int waitTocks,
|
||||
int sequence)
|
||||
{
|
||||
this.Test(
|
||||
x => new WriteLock(x),
|
||||
settleTocks,
|
||||
waitTocks,
|
||||
sequence);
|
||||
}
|
||||
|
||||
private void VerifySequenceRecord(
|
||||
params (int, LockAction BeforeLock)[] values)
|
||||
{
|
||||
lock (this.syncLock)
|
||||
{
|
||||
var compareLogic = new CompareLogic
|
||||
{
|
||||
Config =
|
||||
{
|
||||
IgnoreObjectTypes = true,
|
||||
MaxDifferences = 2,
|
||||
}
|
||||
};
|
||||
ComparisonResult result = compareLogic.Compare(
|
||||
values,
|
||||
this.sequenceRecord);
|
||||
|
||||
if (!result.AreEqual)
|
||||
{
|
||||
throw new XunitException(result.DifferencesString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
tests/MfGames.Locking.Tests/MfGames.Locking.Tests.csproj
Normal file
36
tests/MfGames.Locking.Tests/MfGames.Locking.Tests.csproj
Normal file
|
@ -0,0 +1,36 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CompareNETObjects" Version="4.78.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="JunitXml.TestLogger" Version="3.0.114" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\MfGames.Locking\MfGames.Locking.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
1
tests/README.md
Normal file
1
tests/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Test Projects
|
Reference in a new issue