3 Commits

Author SHA1 Message Date
Patrick Stevens
0b64d3dd34 Atomic file writes (#12) 2025-06-22 23:04:13 +01:00
Patrick Stevens
457d7b16de Add a whole lot more tests for the parser (#11) 2025-06-16 22:44:42 +01:00
Patrick Stevens
d115185525 Use MS.Testing.Platform to run tests (#10) 2025-06-16 21:10:16 +01:00
18 changed files with 2581 additions and 7 deletions

22
.envrc
View File

@@ -1 +1,23 @@
use flake
DOTNET_PATH=$(readlink "$(which dotnet)")
SETTINGS_FILE=$(find . -maxdepth 1 -type f -name '*.sln.DotSettings.user')
MSBUILD=$(realpath "$(find "$(dirname "$DOTNET_PATH")/../share/dotnet/sdk" -maxdepth 2 -type f -name MSBuild.dll)")
if [ -f "$SETTINGS_FILE" ] ; then
xmlstarlet ed --inplace \
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
-N s="clr-namespace:System;assembly=mscorlib" \
-N ss="urn:shemas-jetbrains-com:settings-storage-xaml" \
--update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/DotNetCliExePath/@EntryValue']" \
--value "$(realpath "$(dirname "$DOTNET_PATH")/../share/dotnet/dotnet")" \
"$SETTINGS_FILE"
xmlstarlet ed --inplace \
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
-N s="clr-namespace:System;assembly=mscorlib" \
-N ss="urn:shemas-jetbrains-com:settings-storage-xaml" \
--update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue']" \
--value "$MSBUILD" \
"$SETTINGS_FILE"
fi

View File

@@ -0,0 +1,44 @@
namespace BigExample
open WoofWare.Expect
module MyModule =
let multipleComments () =
expect {
snapshot (* first comment *) (* second comment *)
(* third comment on new line *)
@"test with many comments"
return 123
}
let nestedComments () =
expect {
snapshot (* outer (* inner *) comment *) """nested comment test"""
return "nested"
}
let commentWithSpecialChars () =
expect {
snapshot (* comment with "quotes" and \ backslash *) "regular string"
return "special"
}
let lotsOfWhitespace () =
expect {
snapshot
"string after whitespace"
return "whitespace"
}
let mixedWhitespaceAndComments () =
expect {
snapshotJson (* comment 1 *)
(* comment 2 *)
(* comment 3 *) @"123"
return 123
}

View File

@@ -0,0 +1,69 @@
namespace BigExample
open WoofWare.Expect
module MyModule =
let emptyString () =
expect {
snapshot ""
return ""
}
let emptyVerbatim () =
expect {
snapshot @""
return ""
}
let emptyTripleQuote () =
expect {
snapshot """"""
return ""
}
let onlyWhitespace () =
expect {
snapshot " \t\n "
return "whitespace"
}
let quotesInQuotes () =
expect {
snapshot @"He said ""Hello"" and she said ""Hi"""
return "quotes"
}
let backslashesGalore () =
expect {
snapshot @"C:\Users\Test\Documents\file.txt"
return "path"
}
let veryLongLine () =
expect {
snapshot
@"This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings"
return "long line"
}
let leadingNewlines () =
expect {
snapshot
"""
Starts with newlines"""
return "leading"
}
let trailingNewlines () =
expect {
snapshot
"""Ends with newlines
"""
return "trailing"
}

View File

@@ -0,0 +1,84 @@
namespace BigExample
open WoofWare.Expect
module MyModule =
let veryLongMultiline () =
expect {
snapshot
"""Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
Indented line 11
More indented line 12
Line 13
Line 14
Line 15"""
return "long"
}
let multilineWithEmptyLines () =
expect {
snapshot
@"First line
Third line
Sixth line"
return "empty lines"
}
let multilineWithSpecialChars () =
expect {
snapshot
"""Special chars:
Tab: here
Quotes: "double" and 'single'
Backslash: \ and \\
Unicode: 🎯
Regex: .*+?[]"""
return "special"
}
let multilineJson () =
expect {
snapshotJson
@"{
""name"": ""test"",
""values"": [
1,
2,
3
],
""nested"": {
""deep"": true
}
}"
return
{
name = "test"
values = [ 1 ; 2 ; 3 ]
nested =
{|
deep = true
|}
}
}
let windowsLineEndings () =
expect {
snapshot "Line 1\r\nLine 2\r\nLine 3"
return "crlf"
}

View File

@@ -0,0 +1,28 @@
namespace BigExample
open WoofWare.Expect
module MyModule =
let regexChars () =
expect {
snapshot @"test with regex chars: .*+?[]{}()|^$\ and more"
return 123
}
let regexInTripleQuote () =
expect {
snapshot """regex: .*+?[]{}()|^$\ in triple quotes"""
return 456
}
let regexInRegularString () =
expect {
snapshot "escaped regex: \\.\\*\\+\\?\\[\\]\\{\\}\\(\\)\\|\\^\\$\\\\"
return 789
}
let complexRegexPattern () =
expect {
snapshotJson @"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
return "IP regex"
}

View File

@@ -0,0 +1,47 @@
namespace BigExample
open WoofWare.Expect
module MyModule =
let emoji () =
expect {
snapshot @"Hello 👋 World 🌍 with emoji 🎉🎊"
return 123
}
let chineseCharacters () =
expect {
snapshot """Chinese: 你好世界"""
return "hello"
}
let arabicRTL () =
expect {
snapshot @"Arabic RTL: مرحبا بالعالم"
return "rtl test"
}
let combiningCharacters () =
expect {
// Combining diacritics: e + ́ = é
snapshot "test with combining: e\u0301 and a\u0308"
return "combining"
}
let mixedScripts () =
expect {
snapshotJson @"Mixed: English, русский, 日本語, العربية, emoji 🚀"
return [ "multilingual" ]
}
let zeroWidthChars () =
expect {
snapshot @"Zerowidthspacetest" // Contains U+200B
return "zwsp"
}
let mathSymbols () =
expect {
snapshot """Math: ∀x∈, ∃y: + = 1 |x| 1"""
return "math"
}

View File

@@ -0,0 +1,313 @@
namespace WoofWare.Expect.Test
open WoofWare.Expect
open NUnit.Framework
[<TestFixture>]
[<Parallelizable(ParallelScope.Children)>]
module TestCommentsAndSpacing =
[<OneTimeSetUp>]
let ``Prepare to bulk-update tests`` () =
// GlobalBuilderConfig.enterBulkUpdateMode ()
()
[<OneTimeTearDown>]
let ``Update all tests`` () =
GlobalBuilderConfig.updateAllSnapshots ()
type Dummy = class end
[<Test>]
let ``Multiple comments between snapshot and string`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "CommentsAndSpacing.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let multipleComments () =
expect {
snapshot (* first comment *) (* second comment *)
(* third comment on new line *)
@""updated after many comments""
return 123
}
let nestedComments () =
expect {
snapshot (* outer (* inner *) comment *) """"""nested comment test""""""
return ""nested""
}
let commentWithSpecialChars () =
expect {
snapshot (* comment with ""quotes"" and \ backslash *) ""regular string""
return ""special""
}
let lotsOfWhitespace () =
expect {
snapshot
""string after whitespace""
return ""whitespace""
}
let mixedWhitespaceAndComments () =
expect {
snapshotJson (* comment 1 *)
(* comment 2 *)
(* comment 3 *) @""123""
return 123
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 8 "updated after many comments"
|> String.concat "\n"
}
[<Test>]
let ``Nested comments`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "CommentsAndSpacing.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let multipleComments () =
expect {
snapshot (* first comment *) (* second comment *)
(* third comment on new line *)
@""test with many comments""
return 123
}
let nestedComments () =
expect {
snapshot (* outer (* inner *) comment *) @""updated after nested comments""
return ""nested""
}
let commentWithSpecialChars () =
expect {
snapshot (* comment with ""quotes"" and \ backslash *) ""regular string""
return ""special""
}
let lotsOfWhitespace () =
expect {
snapshot
""string after whitespace""
return ""whitespace""
}
let mixedWhitespaceAndComments () =
expect {
snapshotJson (* comment 1 *)
(* comment 2 *)
(* comment 3 *) @""123""
return 123
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 17 "updated after nested comments"
|> String.concat "\n"
}
[<Test>]
let ``Comment with special chars`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "CommentsAndSpacing.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let multipleComments () =
expect {
snapshot (* first comment *) (* second comment *)
(* third comment on new line *)
@""test with many comments""
return 123
}
let nestedComments () =
expect {
snapshot (* outer (* inner *) comment *) """"""nested comment test""""""
return ""nested""
}
let commentWithSpecialChars () =
expect {
snapshot (* comment with ""quotes"" and \ backslash *) @""updated after weird comment""
return ""special""
}
let lotsOfWhitespace () =
expect {
snapshot
""string after whitespace""
return ""whitespace""
}
let mixedWhitespaceAndComments () =
expect {
snapshotJson (* comment 1 *)
(* comment 2 *)
(* comment 3 *) @""123""
return 123
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 23 "updated after weird comment"
|> String.concat "\n"
}
[<Test>]
let ``Whitespace before`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "CommentsAndSpacing.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let multipleComments () =
expect {
snapshot (* first comment *) (* second comment *)
(* third comment on new line *)
@""test with many comments""
return 123
}
let nestedComments () =
expect {
snapshot (* outer (* inner *) comment *) """"""nested comment test""""""
return ""nested""
}
let commentWithSpecialChars () =
expect {
snapshot (* comment with ""quotes"" and \ backslash *) ""regular string""
return ""special""
}
let lotsOfWhitespace () =
expect {
snapshot
@""updated after spaces""
return ""whitespace""
}
let mixedWhitespaceAndComments () =
expect {
snapshotJson (* comment 1 *)
(* comment 2 *)
(* comment 3 *) @""123""
return 123
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 29 "updated after spaces"
|> String.concat "\n"
}
[<Test>]
let ``Mixed whitespace and comments`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "CommentsAndSpacing.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let multipleComments () =
expect {
snapshot (* first comment *) (* second comment *)
(* third comment on new line *)
@""test with many comments""
return 123
}
let nestedComments () =
expect {
snapshot (* outer (* inner *) comment *) """"""nested comment test""""""
return ""nested""
}
let commentWithSpecialChars () =
expect {
snapshot (* comment with ""quotes"" and \ backslash *) ""regular string""
return ""special""
}
let lotsOfWhitespace () =
expect {
snapshot
""string after whitespace""
return ""whitespace""
}
let mixedWhitespaceAndComments () =
expect {
snapshotJson (* comment 1 *)
(* comment 2 *)
(* comment 3 *) @""updated after comments""
return 123
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 39 "updated after comments"
|> String.concat "\n"
}

View File

@@ -0,0 +1,781 @@
namespace WoofWare.Expect.Test
open NUnit.Framework
open WoofWare.Expect
[<TestFixture>]
module TestEdgeCases =
[<OneTimeSetUp>]
let ``Prepare to bulk-update tests`` () =
// GlobalBuilderConfig.enterBulkUpdateMode ()
()
[<OneTimeTearDown>]
let ``Update all tests`` () =
GlobalBuilderConfig.updateAllSnapshots ()
type Dummy = class end
[<Test>]
let ``Empty string replacements`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emptyString () =
expect {
snapshot @""now has content""
return """"
}
let emptyVerbatim () =
expect {
snapshot @""""
return """"
}
let emptyTripleQuote () =
expect {
snapshot """"""""""""
return """"
}
let onlyWhitespace () =
expect {
snapshot "" \t\n ""
return ""whitespace""
}
let quotesInQuotes () =
expect {
snapshot @""He said """"Hello"""" and she said """"Hi""""""
return ""quotes""
}
let backslashesGalore () =
expect {
snapshot @""C:\Users\Test\Documents\file.txt""
return ""path""
}
let veryLongLine () =
expect {
snapshot
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
return ""long line""
}
let leadingNewlines () =
expect {
snapshot
""""""
Starts with newlines""""""
return ""leading""
}
let trailingNewlines () =
expect {
snapshot
""""""Ends with newlines
""""""
return ""trailing""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 8 "now has content"
|> String.concat "\n"
}
[<Test>]
let ``Empty string replacements, verbatim`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emptyString () =
expect {
snapshot """"
return """"
}
let emptyVerbatim () =
expect {
snapshot @""now has content""
return """"
}
let emptyTripleQuote () =
expect {
snapshot """"""""""""
return """"
}
let onlyWhitespace () =
expect {
snapshot "" \t\n ""
return ""whitespace""
}
let quotesInQuotes () =
expect {
snapshot @""He said """"Hello"""" and she said """"Hi""""""
return ""quotes""
}
let backslashesGalore () =
expect {
snapshot @""C:\Users\Test\Documents\file.txt""
return ""path""
}
let veryLongLine () =
expect {
snapshot
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
return ""long line""
}
let leadingNewlines () =
expect {
snapshot
""""""
Starts with newlines""""""
return ""leading""
}
let trailingNewlines () =
expect {
snapshot
""""""Ends with newlines
""""""
return ""trailing""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 14 "now has content"
|> String.concat "\n"
}
[<Test>]
let ``Empty string replacements, triple quotes`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emptyString () =
expect {
snapshot """"
return """"
}
let emptyVerbatim () =
expect {
snapshot @""""
return """"
}
let emptyTripleQuote () =
expect {
snapshot @""now has content""
return """"
}
let onlyWhitespace () =
expect {
snapshot "" \t\n ""
return ""whitespace""
}
let quotesInQuotes () =
expect {
snapshot @""He said """"Hello"""" and she said """"Hi""""""
return ""quotes""
}
let backslashesGalore () =
expect {
snapshot @""C:\Users\Test\Documents\file.txt""
return ""path""
}
let veryLongLine () =
expect {
snapshot
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
return ""long line""
}
let leadingNewlines () =
expect {
snapshot
""""""
Starts with newlines""""""
return ""leading""
}
let trailingNewlines () =
expect {
snapshot
""""""Ends with newlines
""""""
return ""trailing""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 20 "now has content"
|> String.concat "\n"
}
[<Test>]
let ``Empty string replacements, only whitespace`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emptyString () =
expect {
snapshot """"
return """"
}
let emptyVerbatim () =
expect {
snapshot @""""
return """"
}
let emptyTripleQuote () =
expect {
snapshot """"""""""""
return """"
}
let onlyWhitespace () =
expect {
snapshot @""now has content""
return ""whitespace""
}
let quotesInQuotes () =
expect {
snapshot @""He said """"Hello"""" and she said """"Hi""""""
return ""quotes""
}
let backslashesGalore () =
expect {
snapshot @""C:\Users\Test\Documents\file.txt""
return ""path""
}
let veryLongLine () =
expect {
snapshot
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
return ""long line""
}
let leadingNewlines () =
expect {
snapshot
""""""
Starts with newlines""""""
return ""leading""
}
let trailingNewlines () =
expect {
snapshot
""""""Ends with newlines
""""""
return ""trailing""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 26 "now has content"
|> String.concat "\n"
}
[<Test>]
let ``Quotes in quotes handling`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emptyString () =
expect {
snapshot """"
return """"
}
let emptyVerbatim () =
expect {
snapshot @""""
return """"
}
let emptyTripleQuote () =
expect {
snapshot """"""""""""
return """"
}
let onlyWhitespace () =
expect {
snapshot "" \t\n ""
return ""whitespace""
}
let quotesInQuotes () =
expect {
snapshot @""Updated: He said """"What's up?"""" and replied """"Nothing much.""""""
return ""quotes""
}
let backslashesGalore () =
expect {
snapshot @""C:\Users\Test\Documents\file.txt""
return ""path""
}
let veryLongLine () =
expect {
snapshot
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
return ""long line""
}
let leadingNewlines () =
expect {
snapshot
""""""
Starts with newlines""""""
return ""leading""
}
let trailingNewlines () =
expect {
snapshot
""""""Ends with newlines
""""""
return ""trailing""
}
"
return
SnapshotUpdate.updateSnapshotAtLine
source
32
"Updated: He said \"What's up?\" and replied \"Nothing much.\""
|> String.concat "\n"
}
[<Test>]
let ``Backslashes galore`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emptyString () =
expect {
snapshot """"
return """"
}
let emptyVerbatim () =
expect {
snapshot @""""
return """"
}
let emptyTripleQuote () =
expect {
snapshot """"""""""""
return """"
}
let onlyWhitespace () =
expect {
snapshot "" \t\n ""
return ""whitespace""
}
let quotesInQuotes () =
expect {
snapshot @""He said """"Hello"""" and she said """"Hi""""""
return ""quotes""
}
let backslashesGalore () =
expect {
snapshot @""prefer\these\ones""
return ""path""
}
let veryLongLine () =
expect {
snapshot
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
return ""long line""
}
let leadingNewlines () =
expect {
snapshot
""""""
Starts with newlines""""""
return ""leading""
}
let trailingNewlines () =
expect {
snapshot
""""""Ends with newlines
""""""
return ""trailing""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 38 "prefer\\these\\ones"
|> String.concat "\n"
}
[<Test>]
let ``Very long line`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emptyString () =
expect {
snapshot """"
return """"
}
let emptyVerbatim () =
expect {
snapshot @""""
return """"
}
let emptyTripleQuote () =
expect {
snapshot """"""""""""
return """"
}
let onlyWhitespace () =
expect {
snapshot "" \t\n ""
return ""whitespace""
}
let quotesInQuotes () =
expect {
snapshot @""He said """"Hello"""" and she said """"Hi""""""
return ""quotes""
}
let backslashesGalore () =
expect {
snapshot @""C:\Users\Test\Documents\file.txt""
return ""path""
}
let veryLongLine () =
expect {
snapshot
@""this line is short though""
return ""long line""
}
let leadingNewlines () =
expect {
snapshot
""""""
Starts with newlines""""""
return ""leading""
}
let trailingNewlines () =
expect {
snapshot
""""""Ends with newlines
""""""
return ""trailing""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 44 "this line is short though"
|> String.concat "\n"
}
[<Test>]
let ``Leading newlines`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emptyString () =
expect {
snapshot """"
return """"
}
let emptyVerbatim () =
expect {
snapshot @""""
return """"
}
let emptyTripleQuote () =
expect {
snapshot """"""""""""
return """"
}
let onlyWhitespace () =
expect {
snapshot "" \t\n ""
return ""whitespace""
}
let quotesInQuotes () =
expect {
snapshot @""He said """"Hello"""" and she said """"Hi""""""
return ""quotes""
}
let backslashesGalore () =
expect {
snapshot @""C:\Users\Test\Documents\file.txt""
return ""path""
}
let veryLongLine () =
expect {
snapshot
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
return ""long line""
}
let leadingNewlines () =
expect {
snapshot
@""
Just newlines!
""
return ""leading""
}
let trailingNewlines () =
expect {
snapshot
""""""Ends with newlines
""""""
return ""trailing""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 52 "\n\nJust newlines!\n\n\n"
|> String.concat "\n"
}
[<Test>]
let ``Trailing newlines`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "EdgeCases.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emptyString () =
expect {
snapshot """"
return """"
}
let emptyVerbatim () =
expect {
snapshot @""""
return """"
}
let emptyTripleQuote () =
expect {
snapshot """"""""""""
return """"
}
let onlyWhitespace () =
expect {
snapshot "" \t\n ""
return ""whitespace""
}
let quotesInQuotes () =
expect {
snapshot @""He said """"Hello"""" and she said """"Hi""""""
return ""quotes""
}
let backslashesGalore () =
expect {
snapshot @""C:\Users\Test\Documents\file.txt""
return ""path""
}
let veryLongLine () =
expect {
snapshot
@""This is a very long line that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and contains over 300 characters to test how the parser handles very long single-line strings""
return ""long line""
}
let leadingNewlines () =
expect {
snapshot
""""""
Starts with newlines""""""
return ""leading""
}
let trailingNewlines () =
expect {
snapshot
@""
Just newlines!
""
return ""trailing""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 62 "\n\nJust newlines!\n\n\n"
|> String.concat "\n"
}

View File

@@ -0,0 +1,483 @@
namespace WoofWare.Expect.Test
open NUnit.Framework
open WoofWare.Expect
[<TestFixture>]
module TestMultilineComplex =
[<OneTimeSetUp>]
let ``Prepare to bulk-update tests`` () =
// GlobalBuilderConfig.enterBulkUpdateMode ()
()
[<OneTimeTearDown>]
let ``Update all tests`` () =
GlobalBuilderConfig.updateAllSnapshots ()
type Dummy = class end
[<Test>]
let ``Very long multiline string`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "MultilineComplex.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let veryLongMultiline () =
expect {
snapshot
@""Replaced with
a different
multiline
value""
return ""long""
}
let multilineWithEmptyLines () =
expect {
snapshot
@""First line
Third line
Sixth line""
return ""empty lines""
}
let multilineWithSpecialChars () =
expect {
snapshot
""""""Special chars:
Tab: here
Quotes: ""double"" and 'single'
Backslash: \ and \\
Unicode: 🎯
Regex: .*+?[]""""""
return ""special""
}
let multilineJson () =
expect {
snapshotJson
@""{
""""name"""": """"test"""",
""""values"""": [
1,
2,
3
],
""""nested"""": {
""""deep"""": true
}
}""
return
{
name = ""test""
values = [ 1 ; 2 ; 3 ]
nested =
{|
deep = true
|}
}
}
let windowsLineEndings () =
expect {
snapshot ""Line 1\r\nLine 2\r\nLine 3""
return ""crlf""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 8 "Replaced with\na different\nmultiline\nvalue"
|> String.concat "\n"
}
[<Test>]
let ``Multiline with empty lines`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "MultilineComplex.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let veryLongMultiline () =
expect {
snapshot
""""""Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
Indented line 11
More indented line 12
Line 13
Line 14
Line 15""""""
return ""long""
}
let multilineWithEmptyLines () =
expect {
snapshot
@""Replaced with
a different
multiline
value""
return ""empty lines""
}
let multilineWithSpecialChars () =
expect {
snapshot
""""""Special chars:
Tab: here
Quotes: ""double"" and 'single'
Backslash: \ and \\
Unicode: 🎯
Regex: .*+?[]""""""
return ""special""
}
let multilineJson () =
expect {
snapshotJson
@""{
""""name"""": """"test"""",
""""values"""": [
1,
2,
3
],
""""nested"""": {
""""deep"""": true
}
}""
return
{
name = ""test""
values = [ 1 ; 2 ; 3 ]
nested =
{|
deep = true
|}
}
}
let windowsLineEndings () =
expect {
snapshot ""Line 1\r\nLine 2\r\nLine 3""
return ""crlf""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 30 "Replaced with\n\na different\nmultiline\nvalue"
|> String.concat "\n"
}
[<Test>]
let ``Multiline with special chars`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "MultilineComplex.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let veryLongMultiline () =
expect {
snapshot
""""""Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
Indented line 11
More indented line 12
Line 13
Line 14
Line 15""""""
return ""long""
}
let multilineWithEmptyLines () =
expect {
snapshot
@""First line
Third line
Sixth line""
return ""empty lines""
}
let multilineWithSpecialChars () =
expect {
snapshot
@""get rid of it all""
return ""special""
}
let multilineJson () =
expect {
snapshotJson
@""{
""""name"""": """"test"""",
""""values"""": [
1,
2,
3
],
""""nested"""": {
""""deep"""": true
}
}""
return
{
name = ""test""
values = [ 1 ; 2 ; 3 ]
nested =
{|
deep = true
|}
}
}
let windowsLineEndings () =
expect {
snapshot ""Line 1\r\nLine 2\r\nLine 3""
return ""crlf""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 43 "get rid of it all"
|> String.concat "\n"
}
[<Test>]
let ``Complex nested JSON with Unicode`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "MultilineComplex.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let veryLongMultiline () =
expect {
snapshot
""""""Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
Indented line 11
More indented line 12
Line 13
Line 14
Line 15""""""
return ""long""
}
let multilineWithEmptyLines () =
expect {
snapshot
@""First line
Third line
Sixth line""
return ""empty lines""
}
let multilineWithSpecialChars () =
expect {
snapshot
""""""Special chars:
Tab: here
Quotes: ""double"" and 'single'
Backslash: \ and \\
Unicode: 🎯
Regex: .*+?[]""""""
return ""special""
}
let multilineJson () =
expect {
snapshotJson
@""wheeeee""
return
{
name = ""test""
values = [ 1 ; 2 ; 3 ]
nested =
{|
deep = true
|}
}
}
let windowsLineEndings () =
expect {
snapshot ""Line 1\r\nLine 2\r\nLine 3""
return ""crlf""
}
"
return SnapshotUpdate.updateSnapshotAtLine source 56 "wheeeee" |> String.concat "\n"
}
[<Test>]
let ``Windows line endings`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "MultilineComplex.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let veryLongMultiline () =
expect {
snapshot
""""""Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
Indented line 11
More indented line 12
Line 13
Line 14
Line 15""""""
return ""long""
}
let multilineWithEmptyLines () =
expect {
snapshot
@""First line
Third line
Sixth line""
return ""empty lines""
}
let multilineWithSpecialChars () =
expect {
snapshot
""""""Special chars:
Tab: here
Quotes: ""double"" and 'single'
Backslash: \ and \\
Unicode: 🎯
Regex: .*+?[]""""""
return ""special""
}
let multilineJson () =
expect {
snapshotJson
@""{
""""name"""": """"test"""",
""""values"""": [
1,
2,
3
],
""""nested"""": {
""""deep"""": true
}
}""
return
{
name = ""test""
values = [ 1 ; 2 ; 3 ]
nested =
{|
deep = true
|}
}
}
let windowsLineEndings () =
expect {
snapshot @""down with line endings""
return ""crlf""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 82 "down with line endings"
|> String.concat "\n"
}

View File

@@ -0,0 +1,190 @@
namespace WoofWare.Expect.Test
open WoofWare.Expect
open NUnit.Framework
[<TestFixture>]
[<Parallelizable(ParallelScope.Children)>]
module TestRegexMetacharacters =
[<OneTimeSetUp>]
let ``Prepare to bulk-update tests`` () =
// GlobalBuilderConfig.enterBulkUpdateMode ()
()
[<OneTimeTearDown>]
let ``Update all tests`` () =
GlobalBuilderConfig.updateAllSnapshots ()
type Dummy = class end
[<Test>]
let ``Regex metacharacters in verbatim string`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "RegexMetacharacters.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let regexChars () =
expect {
snapshot @""replacement with .*+?[]{}()|^$\ chars""
return 123
}
let regexInTripleQuote () =
expect {
snapshot """"""regex: .*+?[]{}()|^$\ in triple quotes""""""
return 456
}
let regexInRegularString () =
expect {
snapshot ""escaped regex: \\.\\*\\+\\?\\[\\]\\{\\}\\(\\)\\|\\^\\$\\\\""
return 789
}
let complexRegexPattern () =
expect {
snapshotJson @""^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$""
return ""IP regex""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 8 "replacement with .*+?[]{}()|^$\\ chars"
|> String.concat "\n"
}
[<Test>]
let ``Regex metacharacters in triple quote`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "RegexMetacharacters.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let regexChars () =
expect {
snapshot @""test with regex chars: .*+?[]{}()|^$\ and more""
return 123
}
let regexInTripleQuote () =
expect {
snapshot @""new regex: [a-z]+(?:\d{2,4})? pattern""
return 456
}
let regexInRegularString () =
expect {
snapshot ""escaped regex: \\.\\*\\+\\?\\[\\]\\{\\}\\(\\)\\|\\^\\$\\\\""
return 789
}
let complexRegexPattern () =
expect {
snapshotJson @""^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$""
return ""IP regex""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 14 "new regex: [a-z]+(?:\\d{2,4})? pattern"
|> String.concat "\n"
}
[<Test>]
let ``Regex metacharacters in regular string`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "RegexMetacharacters.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let regexChars () =
expect {
snapshot @""test with regex chars: .*+?[]{}()|^$\ and more""
return 123
}
let regexInTripleQuote () =
expect {
snapshot """"""regex: .*+?[]{}()|^$\ in triple quotes""""""
return 456
}
let regexInRegularString () =
expect {
snapshot @""new regex: [a-z]+(?:\d{2,4})? pattern""
return 789
}
let complexRegexPattern () =
expect {
snapshotJson @""^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$""
return ""IP regex""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 20 "new regex: [a-z]+(?:\\d{2,4})? pattern"
|> String.concat "\n"
}
[<Test>]
let ``IP regex`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "RegexMetacharacters.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let regexChars () =
expect {
snapshot @""test with regex chars: .*+?[]{}()|^$\ and more""
return 123
}
let regexInTripleQuote () =
expect {
snapshot """"""regex: .*+?[]{}()|^$\ in triple quotes""""""
return 456
}
let regexInRegularString () =
expect {
snapshot ""escaped regex: \\.\\*\\+\\?\\[\\]\\{\\}\\(\\)\\|\\^\\$\\\\""
return 789
}
let complexRegexPattern () =
expect {
snapshotJson @""new regex: [a-z]+(?:\d{2,4})? pattern""
return ""IP regex""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 26 "new regex: [a-z]+(?:\\d{2,4})? pattern"
|> String.concat "\n"
}

View File

@@ -4,7 +4,16 @@ open WoofWare.Expect
open NUnit.Framework
[<TestFixture>]
[<Parallelizable(ParallelScope.Children)>]
module TestSnapshotFinding =
[<OneTimeSetUp>]
let ``Prepare to bulk-update tests`` () =
// GlobalBuilderConfig.enterBulkUpdateMode ()
()
[<OneTimeTearDown>]
let ``Update all tests`` () =
GlobalBuilderConfig.updateAllSnapshots ()
type Dummy = class end

View File

@@ -0,0 +1,456 @@
namespace WoofWare.Expect.Test
open NUnit.Framework
open WoofWare.Expect
[<TestFixture>]
module TestUnicodeCharacters =
[<OneTimeSetUp>]
let ``Prepare to bulk-update tests`` () =
// GlobalBuilderConfig.enterBulkUpdateMode ()
()
[<OneTimeTearDown>]
let ``Update all tests`` () =
GlobalBuilderConfig.updateAllSnapshots ()
type Dummy = class end
[<Test>]
let ``Unicode emoji in string`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emoji () =
expect {
snapshot @""Updated with 🚀🌟✨ more emoji!""
return 123
}
let chineseCharacters () =
expect {
snapshot """"""Chinese: 你好世界""""""
return ""hello""
}
let arabicRTL () =
expect {
snapshot @""Arabic RTL: مرحبا بالعالم""
return ""rtl test""
}
let combiningCharacters () =
expect {
// Combining diacritics: e + ́ = é
snapshot ""test with combining: e\u0301 and a\u0308""
return ""combining""
}
let mixedScripts () =
expect {
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
return [ ""multilingual"" ]
}
let zeroWidthChars () =
expect {
snapshot @""Zerowidthspacetest"" // Contains U+200B
return ""zwsp""
}
let mathSymbols () =
expect {
snapshot """"""Math: ∀x∈, ∃y: + = 1 |x| 1""""""
return ""math""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 8 "Updated with 🚀🌟✨ more emoji!"
|> String.concat "\n"
}
[<Test>]
let ``Unicode Chinese characters multi-line`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emoji () =
expect {
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
return 123
}
let chineseCharacters () =
expect {
snapshot @""Chinese poem:
静夜思
床前明月光
疑是地上霜
举头望明月
低头思故乡""
return ""hello""
}
let arabicRTL () =
expect {
snapshot @""Arabic RTL: مرحبا بالعالم""
return ""rtl test""
}
let combiningCharacters () =
expect {
// Combining diacritics: e + ́ = é
snapshot ""test with combining: e\u0301 and a\u0308""
return ""combining""
}
let mixedScripts () =
expect {
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
return [ ""multilingual"" ]
}
let zeroWidthChars () =
expect {
snapshot @""Zerowidthspacetest"" // Contains U+200B
return ""zwsp""
}
let mathSymbols () =
expect {
snapshot """"""Math: ∀x∈, ∃y: + = 1 |x| 1""""""
return ""math""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 14 "Chinese poem:\n静夜思\n床前明月光\n疑是地上霜\n举头望明月\n低头思故乡"
|> String.concat "\n"
}
[<Test>]
let ``Arabic RTL`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emoji () =
expect {
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
return 123
}
let chineseCharacters () =
expect {
snapshot """"""Chinese: 你好世界""""""
return ""hello""
}
let arabicRTL () =
expect {
snapshot @""Updated Arabic: مرحبا بالعالم""
return ""rtl test""
}
let combiningCharacters () =
expect {
// Combining diacritics: e + ́ = é
snapshot ""test with combining: e\u0301 and a\u0308""
return ""combining""
}
let mixedScripts () =
expect {
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
return [ ""multilingual"" ]
}
let zeroWidthChars () =
expect {
snapshot @""Zerowidthspacetest"" // Contains U+200B
return ""zwsp""
}
let mathSymbols () =
expect {
snapshot """"""Math: ∀x∈, ∃y: + = 1 |x| 1""""""
return ""math""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 20 "Updated Arabic: مرحبا بالعالم"
|> String.concat "\n"
}
[<Test>]
let ``Combining characters`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emoji () =
expect {
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
return 123
}
let chineseCharacters () =
expect {
snapshot """"""Chinese: 你好世界""""""
return ""hello""
}
let arabicRTL () =
expect {
snapshot @""Arabic RTL: مرحبا بالعالم""
return ""rtl test""
}
let combiningCharacters () =
expect {
// Combining diacritics: e + ́ = é
snapshot @""updated test with combining: and ä!""
return ""combining""
}
let mixedScripts () =
expect {
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
return [ ""multilingual"" ]
}
let zeroWidthChars () =
expect {
snapshot @""Zerowidthspacetest"" // Contains U+200B
return ""zwsp""
}
let mathSymbols () =
expect {
snapshot """"""Math: ∀x∈, ∃y: + = 1 |x| 1""""""
return ""math""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 27 "updated test with combining: e\u0301 and a\u0308!"
|> String.concat "\n"
}
[<Test>]
let ``Mixed scripts`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emoji () =
expect {
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
return 123
}
let chineseCharacters () =
expect {
snapshot """"""Chinese: 你好世界""""""
return ""hello""
}
let arabicRTL () =
expect {
snapshot @""Arabic RTL: مرحبا بالعالم""
return ""rtl test""
}
let combiningCharacters () =
expect {
// Combining diacritics: e + ́ = é
snapshot ""test with combining: e\u0301 and a\u0308""
return ""combining""
}
let mixedScripts () =
expect {
snapshotJson @""Updated mixed: English, русский, 日本語, العربية, emoji 🚀""
return [ ""multilingual"" ]
}
let zeroWidthChars () =
expect {
snapshot @""Zerowidthspacetest"" // Contains U+200B
return ""zwsp""
}
let mathSymbols () =
expect {
snapshot """"""Math: ∀x∈, ∃y: + = 1 |x| 1""""""
return ""math""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 33 "Updated mixed: English, русский, 日本語, العربية, emoji 🚀"
|> String.concat "\n"
}
[<Test>]
let ``ZWBS character`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emoji () =
expect {
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
return 123
}
let chineseCharacters () =
expect {
snapshot """"""Chinese: 你好世界""""""
return ""hello""
}
let arabicRTL () =
expect {
snapshot @""Arabic RTL: مرحبا بالعالم""
return ""rtl test""
}
let combiningCharacters () =
expect {
// Combining diacritics: e + ́ = é
snapshot ""test with combining: e\u0301 and a\u0308""
return ""combining""
}
let mixedScripts () =
expect {
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
return [ ""multilingual"" ]
}
let zeroWidthChars () =
expect {
snapshot @""Updated: Zerowidthspacetest"" // Contains U+200B
return ""zwsp""
}
let mathSymbols () =
expect {
snapshot """"""Math: ∀x∈, ∃y: + = 1 |x| 1""""""
return ""math""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 39 "Updated: Zerowidthspacetest"
|> String.concat "\n"
}
[<Test>]
let ``Maths`` () =
let source =
Assembly.getEmbeddedResource typeof<Dummy>.Assembly "UnicodeCharacters.fs"
|> _.Split('\n')
expect {
snapshot
@"namespace BigExample
open WoofWare.Expect
module MyModule =
let emoji () =
expect {
snapshot @""Hello 👋 World 🌍 with emoji 🎉🎊""
return 123
}
let chineseCharacters () =
expect {
snapshot """"""Chinese: 你好世界""""""
return ""hello""
}
let arabicRTL () =
expect {
snapshot @""Arabic RTL: مرحبا بالعالم""
return ""rtl test""
}
let combiningCharacters () =
expect {
// Combining diacritics: e + ́ = é
snapshot ""test with combining: e\u0301 and a\u0308""
return ""combining""
}
let mixedScripts () =
expect {
snapshotJson @""Mixed: English, русский, 日本語, العربية, emoji 🚀""
return [ ""multilingual"" ]
}
let zeroWidthChars () =
expect {
snapshot @""Zerowidthspacetest"" // Contains U+200B
return ""zwsp""
}
let mathSymbols () =
expect {
snapshot @""Pretty vacuous, huh: ∀x∈, ∃y: + = 1 |x| 1""
return ""math""
}
"
return
SnapshotUpdate.updateSnapshotAtLine source 45 "Pretty vacuous, huh: ∀x∈, ∃y: + = 1 |x| 1"
|> String.concat "\n"
}

View File

@@ -1,21 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
<EnableNUnitRunner>true</EnableNUnitRunner>
<OutputType>Exe</OutputType>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
</PropertyGroup>
<ItemGroup>
<Compile Include="Assembly.fs" />
<Compile Include="BulkUpdateExample.fs" />
<Compile Include="SimpleTest.fs" />
<Compile Include="TestSnapshotFinding.fs" />
<Compile Include="TestSurface.fs" />
<Compile Include="TestSnapshotFinding\TestSnapshotFinding.fs" />
<Compile Include="TestSnapshotFinding\TestUnicodeCharacters.fs" />
<Compile Include="TestSnapshotFinding\TestMultilineComplex.fs" />
<Compile Include="TestSnapshotFinding\TestEdgeCases.fs" />
<Compile Include="TestSnapshotFinding\TestCommentsAndSpacing.fs" />
<Compile Include="TestSnapshotFinding\TestRegexMetacharacters.fs" />
<EmbeddedResource Include="SyntaxCases\AtStringOneLine.fs" />
<EmbeddedResource Include="SyntaxCases\SingleQuoteManyLine.fs" />
<EmbeddedResource Include="SyntaxCases\TripleQuoteInterveningComment.fs" />
<EmbeddedResource Include="SyntaxCases\TripleQuoteOneLine.fs" />
<EmbeddedResource Include="SyntaxCases\RegexMetacharacters.fs" />
<EmbeddedResource Include="SyntaxCases\UnicodeCharacters.fs" />
<EmbeddedResource Include="SyntaxCases\MultilineComplex.fs" />
<EmbeddedResource Include="SyntaxCases\EdgeCases.fs" />
<EmbeddedResource Include="SyntaxCases\CommentsAndSpacing.fs" />
</ItemGroup>
<ItemGroup>

View File

@@ -270,7 +270,7 @@ type ExpectBuilder (mode : Mode) =
let lines = File.ReadAllLines state.Caller.FilePath
let oldContents = String.concat "\n" lines
let lines = SnapshotUpdate.updateSnapshotAtLine lines state.Caller.LineNumber actual
File.WriteAllLines (state.Caller.FilePath, lines)
File.writeAllLines lines state.Caller.FilePath
failwith ("Snapshot successfully updated. Previous contents:\n" + oldContents)
match CompletedSnapshotGeneric.passesAssertion state with

32
WoofWare.Expect/File.fs Normal file
View File

@@ -0,0 +1,32 @@
namespace WoofWare.Expect
open System
open System.IO
[<RequireQualifiedAccess>]
module internal File =
/// Standard attempt at an atomic file write.
/// It may fail to be atomic if the working directory somehow spans multiple volumes,
/// and of course with external network storage all bets are off.
let writeAllLines (lines : string[]) (path : string) : unit =
let file = FileInfo path
let tempFile =
Path.Combine (file.Directory.FullName, file.Name + "." + Guid.NewGuid().ToString () + ".tmp")
try
File.WriteAllLines (tempFile, lines)
// Atomicity guarantees are undocumented, but on Unix this is an atomic `rename` call
// https://github.com/dotnet/runtime/blob/9a4be5b56d81aa04c7ea687c02b3f4e64c83761b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs#L181
// and on Windows this is an atomic ReplaceFile:
// https://github.com/dotnet/runtime/blob/9a4be5b56d81aa04c7ea687c02b3f4e64c83761b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs#L92
// calls https://github.com/dotnet/runtime/blob/9a4be5b56d81aa04c7ea687c02b3f4e64c83761b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReplaceFile.cs#L12
// which calls ReplaceFileW, whose atomicity guarantees are again apparently undocumented,
// but 4o-turbo, Opus 4, and Gemini 2.5 Flash all think it's atomic.
File.Replace (tempFile, path, null)
finally
try
File.Delete tempFile
with _ ->
()

View File

@@ -141,7 +141,7 @@ module internal SnapshotUpdate =
else
// We need to include enough lines to capture multi-line strings
// Take a reasonable number of lines after the snapshot line
let maxLines = min 50 (lines.Length - startIdx)
let maxLines = lines.Length - startIdx
let relevantLines = lines |> Array.skip startIdx |> Array.take maxLines
let searchText = String.concat "\n" relevantLines
@@ -300,5 +300,5 @@ module internal SnapshotUpdate =
let newContents = updateAllLines contents sources
System.IO.File.WriteAllLines (callerFile, newContents)
File.writeAllLines newContents callerFile
)

View File

@@ -18,6 +18,7 @@
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Text.fs" />
<Compile Include="File.fs" />
<Compile Include="Domain.fs" />
<Compile Include="SnapshotUpdate.fs" />
<Compile Include="Config.fs" />

View File

@@ -65,6 +65,7 @@
pkgs.alejandra
pkgs.nodePackages.markdown-link-check
pkgs.shellcheck
pkgs.xmlstarlet
];
};
});