Rules/Test-DocumentationQuality.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
function Test-DocumentationQuality
{
    #region ScriptTokenValidation Parameter Statement
    param(
    <#
    This parameter will contain the tokens in the script, and will be automatically
    provided when this command is run within ScriptCop.
     
    This parameter should not be used directly, except for testing purposes.
    #>

    [Parameter(ParameterSetName='TestScriptToken',
        Mandatory=$true,
        ValueFromPipelineByPropertyName=$true)]
    [Management.Automation.PSToken[]]
    $ScriptToken,
    
    <#
    This parameter will contain the command that was tokenized, and will be automatically
    provided when this command is run within ScriptCop.
     
    This parameter should not be used directly, except for testing purposes.
    #>

    [Parameter(ParameterSetName='TestScriptToken',Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    [Management.Automation.CommandInfo]
    $ScriptTokenCommand,
    
    <#
    This parameter contains the raw text of the script, and will be automatically
    provided when this command is run within ScriptCop
     
    This parameter should not be used directly, except for testing purposes.
    #>

    [Parameter(ParameterSetName='TestScriptToken',Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    [string]
    $ScriptText
    )
    #endregion ScriptTokenValidation Parameter Statement
                
    
    process {    
        #region Comment Ratio
    
        # A quick tight little loop to sum
        # the lengths of different types in one
        # pass (Group-Object would work, but would be slower)
        $commentLength= 0
        $otherLength = 0
        $totalLength = 0 
        $TotalStringLength = 0 
        foreach ($token in $ScriptToken) {
            $totalLength+=$token.Length
            if ($token.Type -eq 'Comment') {
                $commentLength+=$token.Length
            } elseif ($token.Type -eq 'String') {
                $TotalStringLength+=$token.Length
            } else {
                $otherLength+=$token.Length
            }
        }
        
        # The percent is easy to calculate
        $percent =$commentLength * 100 / $totalLength 

        # As for arriving @ the RIGHT %, I asked StackOverflow:
        # http://stackoverflow.com/questions/111563/whats-the-golden-code-comment-ratio
        # Realized people could not agree, and that the low numbers thrown out there
        # had the best intent (say something, anything), and so picked 7.5%
        
        # Then realized inline help and regions would buck the trend a little, and
        # tried 12.5%.
        
        # Noticed a bunch of my own code that had good help, but little inline docs,
        # and settled upon one of two time-honored ratios.
        # Pareto Principle, aka the 80/20 ratio
        # http://en.wikipedia.org/wiki/Pareto_principle
        if ($percent -lt 20) {
            Write-Error "Code is sparsely documented (Only $([Math]::Round($percent,2)) % comments)."
        }
        
        #endregion Comment Ratio
        
        
        #region Check For Regions
        
        $hasRegion = $ScriptToken |
            Where-Object { 
                $_.Type -eq "Comment" -and (
                    $_.Content -like "#region*" -or 
                    $_.Content -like "#endregion*"
                ) 
            }
        
        if (-not $hasRegion) {
            $errorParams = @{
                Message ="$ScriptTokenCommand does not define any #regions"
                RecommendedAction="Add Regions"
                ErrorId="TestDocumentationQuality.AddRegions" 
            }
            Write-Error @errorParams
            return
        }
        
        #endregions
        
        
        #region Common Documentation Help Mistakes
        
        $dotExamples = $ScriptToken |
            Where-Object { 
                $_.Type -eq "Comment" -and $_.Content -like "*.examples*"
            }
            
        if ($dotExamples) {
            @{
                Message ="$ScriptTokenCommand has a common typo in comment based help, .EXAMPLES should be .EXAMPLE"                
                ErrorId="TestDocumentationQuality.DotExamples" 
                TargetObject = $dotExamples
            }
            return
        }
        

        $dotNote = $ScriptToken |
            Where-Object { 
                $_.Type -eq "Comment" -and $_.Content -like "*.note*" -and $_.Content -notlike "*.notes*"
            }
            
        if ($dotNote) {
            @{
                Message ="$ScriptTokenCommand has a common typo in comment based help, .NOTE should be .NOTES"                
                ErrorId="TestDocumentationQuality.DotNote" 
                TargetObject = $dotNote
            }
            return
        }


        $dotLinks = $ScriptToken |
            Where-Object { 
                $_.Type -eq "Comment" -and $_.Content -like "*.links*"
            }
            
        if ($dotLinks ) {
            @{
                Message ="$ScriptTokenCommand has a common typo in comment based help, .LINKS should be .LINK"                
                ErrorId="TestDocumentationQuality.DotLinks" 
                TargetObject = $dotLinks
            }
            return
        }

        #endregion
        
        
    }
}