-
-
Notifications
You must be signed in to change notification settings - Fork 187
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
win, compiler: fix and validate comment-only code
This commit fixes scripts using `DeleteRegistryKey` having empty revert codes. It also adds validation in compiler to prevent genarating scripts composed solely of comments.
- Loading branch information
1 parent
92ec138
commit 2f2813e
Showing
16 changed files
with
422 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
src/application/Parser/Executable/Script/Validation/Analyzers/AnalyzeCommentOnlyCode.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import type { ScriptingLanguage } from '@/domain/ScriptingLanguage'; | ||
import { isCommentLine, type CommentLineChecker } from './Common/CommentLineChecker'; | ||
import { createSyntax, type SyntaxFactory } from './Syntax/SyntaxFactory'; | ||
import type { CodeLine, CodeValidationAnalyzer, InvalidCodeLine } from './CodeValidationAnalyzer'; | ||
|
||
export type CommentOnlyCodeAnalyzer = CodeValidationAnalyzer & { | ||
( | ||
...args: [ | ||
...Parameters<CodeValidationAnalyzer>, | ||
syntaxFactory?: SyntaxFactory, | ||
commentLineChecker?: CommentLineChecker, | ||
] | ||
): ReturnType<CodeValidationAnalyzer>; | ||
}; | ||
|
||
export const analyzeCommentOnlyCode: CommentOnlyCodeAnalyzer = ( | ||
lines: readonly CodeLine[], | ||
language: ScriptingLanguage, | ||
syntaxFactory: SyntaxFactory = createSyntax, | ||
commentLineChecker: CommentLineChecker = isCommentLine, | ||
) => { | ||
const syntax = syntaxFactory(language); | ||
if (!lines.every((line) => commentLineChecker(line.text, syntax))) { | ||
return []; | ||
} | ||
return lines | ||
.map((line): InvalidCodeLine => ({ | ||
lineNumber: line.lineNumber, | ||
error: (() => { | ||
return 'Code consists of comments only'; | ||
})(), | ||
})); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
src/application/Parser/Executable/Script/Validation/Analyzers/Common/CommentLineChecker.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import type { LanguageSyntax } from '../Syntax/LanguageSyntax'; | ||
|
||
export interface CommentLineChecker { | ||
( | ||
codeLine: string, | ||
syntax: LanguageSyntax, | ||
): boolean; | ||
} | ||
|
||
export const isCommentLine: CommentLineChecker = (codeLine, syntax) => { | ||
return syntax.commentDelimiters.some( | ||
(delimiter) => codeLine.toLowerCase().startsWith(delimiter.toLowerCase()), | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ export enum CodeValidationRule { | |
NoEmptyLines, | ||
NoDuplicatedLines, | ||
NoTooLongLines, | ||
NoCommentOnlyLines, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
.../application/Parser/Executable/Script/Validation/Analyzers/AnalyzeCommentOnlyCode.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import { describe, it } from 'vitest'; | ||
import { analyzeCommentOnlyCode, type CommentOnlyCodeAnalyzer } from '@/application/Parser/Executable/Script/Validation/Analyzers/AnalyzeCommentOnlyCode'; | ||
import type { CommentLineChecker } from '@/application/Parser/Executable/Script/Validation/Analyzers/Common/CommentLineChecker'; | ||
import type { SyntaxFactory } from '@/application/Parser/Executable/Script/Validation/Analyzers/Syntax/SyntaxFactory'; | ||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage'; | ||
import type { CodeLine, InvalidCodeLine } from '@/application/Parser/Executable/Script/Validation/Analyzers/CodeValidationAnalyzer'; | ||
import { CommentLineCheckerStub } from '@tests/unit/shared/Stubs/CommentLineCheckerStub'; | ||
import { SyntaxFactoryStub } from '@tests/unit/shared/Stubs/SyntaxFactoryStub'; | ||
import { LanguageSyntaxStub } from '@tests/unit/shared/Stubs/LanguageSyntaxStub'; | ||
import { createCodeLines } from './CreateCodeLines'; | ||
import { expectSameInvalidCodeLines } from './ExpectSameInvalidCodeLines'; | ||
|
||
describe('AnalyzeCommentOnlyCode', () => { | ||
describe('analyzeCommentOnlyCode', () => { | ||
it('returns empty given no match', () => { | ||
// arrange | ||
const context = setupScenario({ | ||
givenLines: ['line-1', 'line-2', 'line-3'], | ||
matchedLines: [], | ||
}); | ||
// act | ||
const actualResult = context.analyze(); | ||
// assert | ||
expect(actualResult).to.have.lengthOf(0); | ||
}); | ||
it('returns empty given some matches', () => { | ||
// arrange | ||
const context = setupScenario({ | ||
givenLines: ['line-1', 'line-2'], | ||
matchedLines: [], | ||
}); | ||
// act | ||
const actualResult = context.analyze(); | ||
// assert | ||
expect(actualResult).to.have.lengthOf(0); | ||
}); | ||
it('returns all lines given all match', () => { | ||
// arrange | ||
const lines = ['line-1', 'line-2', 'line-3']; | ||
const expectedResult: InvalidCodeLine[] = lines | ||
.map((_line, index): InvalidCodeLine => ({ | ||
lineNumber: index + 1, | ||
error: 'Code consists of comments only', | ||
})); | ||
const context = setupScenario({ | ||
givenLines: lines, | ||
matchedLines: lines, | ||
}); | ||
// act | ||
const actualResult = context.analyze(); | ||
// assert | ||
expectSameInvalidCodeLines(expectedResult, actualResult); | ||
}); | ||
it('uses correct language for syntax creation', () => { | ||
// arrange | ||
const expectedLanguage = ScriptingLanguage.batchfile; | ||
let actualLanguage: ScriptingLanguage | undefined; | ||
const factory: SyntaxFactory = (language) => { | ||
actualLanguage = language; | ||
return new LanguageSyntaxStub(); | ||
}; | ||
const context = new TestContext() | ||
.withLanguage(expectedLanguage) | ||
.withSyntaxFactory(factory); | ||
// act | ||
context.analyze(); | ||
// assert | ||
expect(actualLanguage).to.equal(expectedLanguage); | ||
}); | ||
}); | ||
}); | ||
|
||
interface CommentOnlyCodeAnalysisTestScenario { | ||
readonly givenLines: readonly string[]; | ||
readonly matchedLines: readonly string[]; | ||
} | ||
|
||
function setupScenario( | ||
scenario: CommentOnlyCodeAnalysisTestScenario, | ||
): TestContext { | ||
// arrange | ||
const lines = scenario.givenLines; | ||
const syntax = new LanguageSyntaxStub(); | ||
const checker = new CommentLineCheckerStub(); | ||
scenario.matchedLines.forEach((line) => checker.withPredeterminedResult({ | ||
givenLine: line, | ||
givenSyntax: syntax, | ||
result: true, | ||
})); | ||
return new TestContext() | ||
.withSyntaxFactory(() => syntax) | ||
.withLines(lines) | ||
.withCommentLineChecker(checker.get()); | ||
} | ||
|
||
export class TestContext { | ||
private codeLines: readonly CodeLine[] = createCodeLines(['test-code-line']); | ||
|
||
private language = ScriptingLanguage.batchfile; | ||
|
||
private syntaxFactory: SyntaxFactory = new SyntaxFactoryStub().get(); | ||
|
||
private commentLineChecker: CommentLineChecker = new CommentLineCheckerStub().get(); | ||
|
||
public withLines(lines: readonly string[]): this { | ||
this.codeLines = createCodeLines(lines); | ||
return this; | ||
} | ||
|
||
public withLanguage(language: ScriptingLanguage): this { | ||
this.language = language; | ||
return this; | ||
} | ||
|
||
public withSyntaxFactory(syntaxFactory: SyntaxFactory): this { | ||
this.syntaxFactory = syntaxFactory; | ||
return this; | ||
} | ||
|
||
public withCommentLineChecker(commentLineChecker: CommentLineChecker): this { | ||
this.commentLineChecker = commentLineChecker; | ||
return this; | ||
} | ||
|
||
public analyze(): ReturnType<CommentOnlyCodeAnalyzer> { | ||
return analyzeCommentOnlyCode( | ||
this.codeLines, | ||
this.language, | ||
this.syntaxFactory, | ||
this.commentLineChecker, | ||
); | ||
} | ||
} |
Oops, something went wrong.