Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 142 additions & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,10 +1,149 @@
## IDE-independent coding style via EditorConfig: https://editorconfig.org/

# top-most EditorConfig file
root = true

###############################################################################
# Universal defaults (all files)
###############################################################################
[*]
charset = utf-8
indent_style = space
indent_size = 4
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

###############################################################################
# XML / MSBuild project & config files
###############################################################################
[*.{xml,props,targets,csproj,vcxproj,vcxproj.filters,appxmanifest,manifest,resw,resx,config,nuspec,vsixmanifest,slnx}]
indent_size = 2

###############################################################################
# JSON / YAML config files
###############################################################################
[*.{json,json5,yml,yaml}]
indent_size = 2

###############################################################################
# Markdown (preserve trailing whitespace for hard line breaks)
###############################################################################
[*.md]
trim_trailing_whitespace = false

###############################################################################
# C# style and analyzer policy
###############################################################################
[*.cs]
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_indent_labels = one_less_than_current
csharp_style_throw_expression = true:suggestion
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_prefer_top_level_statements = true:silent

# 'this.' qualification: prefer no qualifier (IDE0003). Promoted to warning and
# enforced in CI via build\scripts\VerifyCodeStyle.ps1 (dotnet format), since
# csc does not reliably surface IDE-prefixed analyzers at build time.
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:warning
dotnet_diagnostic.IDE0003.severity = warning

###############################################################################
# .NET naming & code-style (C# and VB)
###############################################################################
[*.{cs,vb}]
#### Naming styles ####

# Naming rules

dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i

dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case

dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case

# Symbol specifications

dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =

dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =

dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =

# Naming styles

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

#### .NET code-style preferences ####

dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
tab_width = 4
indent_size = 4

###############################################################################
# C++ - Visual C++ Include Cleanup settings
###############################################################################
[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]

cpp_include_cleanup_add_missing_error_tag_type = suggestion
cpp_include_cleanup_remove_unused_error_tag_type = dimmed
cpp_include_cleanup_sort_after_edits = true
cpp_sort_includes_error_tag_type = warning
cpp_sort_includes_priority_case_sensitive = false
cpp_sort_includes_priority_style = angle_brackets
cpp_includes_style = default
cpp_includes_use_forward_slash = true
13 changes: 13 additions & 0 deletions .github/workflows/action-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ on:
branches: [main, release/**, feature/**]
workflow_dispatch:
jobs:
verifyCodeStyle:
name: Verify code style (IDE0003)
runs-on: windows-2025-vs2026
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
name: Setup .NET SDK
with:
dotnet-version: '10.0.x'
- run: ./build/scripts/VerifyCodeStyle.ps1
shell: pwsh
name: Verify no unnecessary this. (IDE0003)

defineBuilds:
name: Define builds
runs-on: windows-2025-vs2026
Expand Down
115 changes: 115 additions & 0 deletions build/scripts/VerifyCodeStyle.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
<#
.SYNOPSIS
Verifies that the C# source is free of unnecessary 'this.' qualifications
(IDE0003), enforcing the '.editorconfig' single-source-of-truth policy.

.DESCRIPTION
The Roslyn IDE-prefixed analyzers (IDE0003 et al.) ship with the .NET SDK
but are not reliably surfaced by csc.exe at build time. The 'dotnet format
style' host runs the same Roslyn analyzer and honors the '.editorconfig'
severity ('dotnet_diagnostic.IDE0003.severity = warning'), so this script
uses it as the build-time enforcement gate.

'dotnet format' uses the SDK MSBuild workspace loader, which cannot load
old-style UWP projects (the main Calculator app and its managed view
models) or the native C++ test projects. Only SDK-style C# projects are
listed in the allowlist below; all projects share the same '.editorconfig'
rules. The allowlist is a positive list, so new projects are
safe-by-default until explicitly added.

Restore is performed explicitly per project (and its exit code checked)
before formatting: the allowlisted projects carry PackageReference
dependencies that must be restored to load in the workspace, and a restore
failure should fail the gate loudly rather than letting 'dotnet format'
proceed against a partially loaded workspace.

.PARAMETER Diagnostics
Comma-separated diagnostic IDs to enforce. Defaults to 'IDE0003'.

.EXAMPLE
.\build\scripts\VerifyCodeStyle.ps1
Verifies the allowlisted SDK-style C# projects against IDE0003.

.EXAMPLE
.\build\scripts\VerifyCodeStyle.ps1 -Diagnostics 'IDE0003,IDE0005'
Also enforces IDE0005 (remove unnecessary usings).
#>
[CmdletBinding()]
param(
[string] $Diagnostics = 'IDE0003'
)

$ErrorActionPreference = 'Stop'

# Resolve the repo root (this script lives in <root>\build\scripts).
$root = if ($env:BUILD_SOURCESDIRECTORY) { $env:BUILD_SOURCESDIRECTORY } else { (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path }

# SDK-style C# projects that 'dotnet format' can load. The old-style UWP
# projects (Calculator, Calculator.ManagedViewModels) and the native C++ test
# projects (CalculatorUnitTests) are excluded because the dotnet CLI workspace
# loader cannot evaluate them.
$sdkProjects = @(
'src\CalculatorUITestFramework\CalculatorUITestFramework.csproj',
'src\CalculatorUITests\CalculatorUITests.csproj'
)

Write-Host "Verifying code style for diagnostics: $Diagnostics"
Write-Host "Tooling: dotnet $(dotnet --version)"
Write-Host "Repo root: $root"
Write-Host ""
Write-Host "SDK-style projects to check:"
$sdkProjects | ForEach-Object { Write-Host " $_" }
Write-Host ""

$failed = $false

foreach ($relPath in $sdkProjects)
{
$projPath = Join-Path $root $relPath

if (-not (Test-Path $projPath))
{
Write-Host "##[error]Allowlisted project not found: $relPath"
$failed = $true
continue
}

Write-Host "Checking: $relPath"

# Restore explicitly first so a restore/feed failure fails the gate loudly,
# rather than letting 'dotnet format' run against a partially loaded
# workspace and report success without analyzing anything.
& dotnet restore $projPath

if ($LASTEXITCODE -ne 0)
{
Write-Host "##[error]Restore failed for: $relPath"
$failed = $true
continue
}

& dotnet format style $projPath `
--no-restore `
--verify-no-changes `
--severity warn `
--diagnostics $Diagnostics

if ($LASTEXITCODE -ne 0)
{
Write-Host "##[error]Code style violation ($Diagnostics) in: $relPath"
$failed = $true
}
}

if ($failed)
{
Write-Host ''
Write-Host "##[error]Code style violations detected ($Diagnostics)."
Write-Host '##[error]Run the following locally to fix:'
Write-Host "##[error] dotnet format style <project.csproj> --severity warn --diagnostics $Diagnostics"
exit 1
}

Write-Host ''
Write-Host "##[section]Code style verification passed (no $Diagnostics violations)."
22 changes: 11 additions & 11 deletions src/CalculatorUITestFramework/CalculatorDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ private CalculatorDriver()

public void SetupCalculatorSession(TestContext context)
{
this.server = new WinAppDriverLocalServer();
server = new WinAppDriverLocalServer();

// Launch Calculator application if it is not yet launched
if (this.CalculatorSession == null)
if (CalculatorSession == null)
{
// Create a new WinAppDriver session to bring up an instance of the Calculator application
// Note: Multiple calculator windows (instances) share the same process Id
Expand All @@ -57,25 +57,25 @@ public void SetupCalculatorSession(TestContext context)
}

options.AddAdditionalCapability("deviceName", "WindowsPC");
this.CalculatorSession = new WindowsDriver<WindowsElement>(this.server.ServiceUrl, options);
this.CalculatorSession.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Assert.IsNotNull(this.CalculatorSession);
CalculatorSession = new WindowsDriver<WindowsElement>(server.ServiceUrl, options);
CalculatorSession.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Assert.IsNotNull(CalculatorSession);
}
}

public void TearDownCalculatorSession()
{
// Close the application and delete the session
if (this.CalculatorSession != null)
if (CalculatorSession != null)
{
this.CalculatorSession.Quit();
this.CalculatorSession = null;
CalculatorSession.Quit();
CalculatorSession = null;
}

if (this.server != null)
if (server != null)
{
this.server.Dispose();
this.server = null;
server.Dispose();
server = null;
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions src/CalculatorUITestFramework/CalculatorResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ namespace CalculatorUITestFramework
public class CalculatorResults
{
private WindowsDriver<WindowsElement> session => CalculatorDriver.Instance.CalculatorSession;
private WindowsElement CalculatorAlwaysOnTopResults => this.session.TryFindElementByAccessibilityId("CalculatorAlwaysOnTopResults");
private WindowsElement CalculatorResult => this.session.TryFindElementByAccessibilityId("CalculatorResults");
private WindowsElement CalculatorExpression => this.session.TryFindElementByAccessibilityId("CalculatorExpression");
private WindowsElement CalculatorAlwaysOnTopResults => session.TryFindElementByAccessibilityId("CalculatorAlwaysOnTopResults");
private WindowsElement CalculatorResult => session.TryFindElementByAccessibilityId("CalculatorResults");
private WindowsElement CalculatorExpression => session.TryFindElementByAccessibilityId("CalculatorExpression");

/// <summary>
/// Gets the text from the display control in AoT mode and removes the narrator text that is not displayed in the UI.
/// </summary>
/// <returns>The string shown in the UI.</returns>
public string GetAoTCalculatorResultText()
{
return this.CalculatorAlwaysOnTopResults.Text.Replace("Display is", string.Empty).Trim();
return CalculatorAlwaysOnTopResults.Text.Replace("Display is", string.Empty).Trim();
}

/// <summary>
Expand All @@ -30,7 +30,7 @@ public string GetAoTCalculatorResultText()
/// <returns>The string shown in the UI.</returns>
public string GetCalculatorResultText()
{
return this.CalculatorResult.Text.Replace("Display is", string.Empty).Trim();
return CalculatorResult.Text.Replace("Display is", string.Empty).Trim();
}

/// <summary>
Expand All @@ -39,7 +39,7 @@ public string GetCalculatorResultText()
/// <returns>The string shown in the UI.</returns>
public string GetCalculatorExpressionText()
{
return this.CalculatorExpression.Text.Replace("Expression is", string.Empty).Trim();
return CalculatorExpression.Text.Replace("Expression is", string.Empty).Trim();
}

/// <summary>
Expand All @@ -48,7 +48,7 @@ public string GetCalculatorExpressionText()
/// <returns>The string shown in the UI.</returns>
public void IsResultsDisplayPresent()
{
Assert.IsNotNull(this.CalculatorResult);
Assert.IsNotNull(CalculatorResult);
}

/// <summary>
Expand Down
Loading
Loading