tests/STONWriterTests.st
changeset 0 8f9f6be6af89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/STONWriterTests.st	Tue Jun 04 12:33:53 2019 +0100
@@ -0,0 +1,428 @@
+"{ Package: 'stx:goodies/ston/tests' }"
+
+"{ NameSpace: Smalltalk }"
+
+TestCase subclass:#STONWriterTests
+	instanceVariableNames:''
+	classVariableNames:''
+	poolDictionaries:''
+	category:'STON-Tests-Writer'
+!
+
+!STONWriterTests methodsFor:'private'!
+
+serialize: anObject
+	^ String streamContents: [ :stream |
+		STON writer 
+			on: stream;
+			nextPut: anObject ]
+!
+
+serializeAsciiOnly: anObject
+	^ String streamContents: [ :stream |
+		STON writer 
+			on: stream;
+			asciiOnly: true;
+			nextPut: anObject ]
+!
+
+serializeJson: anObject
+	^ String streamContents: [ :stream |
+		STON jsonWriter 
+			on: stream; 
+			nextPut: anObject ]
+!
+
+serializePretty: anObject
+	^ String streamContents: [ :stream |
+		STON writer 
+			on: stream; 
+			prettyPrint: true;
+			nextPut: anObject ]
+! !
+
+!STONWriterTests methodsFor:'tests'!
+
+testAssociation
+	self assert: (self serialize: 'foo' -> 1) =  '''foo'':1'.
+	self assert: (self serialize: #bar -> 2) =  '#bar:2'.
+	self assert: (self serialize: 'foo bar' -> #ok) =  '''foo bar'':#ok'.
+	self assert: (self serialize: 123 -> 456) =  '123:456'
+!
+
+testBag
+	self 
+		assert: (self serialize: (Bag withAll: #(a a)))
+		equals: 'Bag{#a:2}'.
+	self 
+		assert: (self serialize: Bag new)
+		equals: 'Bag{}'
+!
+
+testBoolean
+	self assert: (self serialize: true) = 'true'.
+	self assert: (self serialize: false) = 'false'
+!
+
+testByteArray
+	self assert: (self serialize: #(1 2 3) asByteArray) = 'ByteArray[''010203'']' 
+!
+
+testClass
+	self assert: (self serialize: Point) = 'Class[#Point]'
+!
+
+testColor
+        self 
+                assert: (self serialize: Color red) 
+                equals: 'Color[#red]'.
+        self 
+                assert: (self serialize: (Color red copy alpha: 0.4)) 
+                equals: 'Color{#red:1.0,#green:0.0,#blue:0.0,#alpha:0.4}'.
+        self 
+                assert: (self serialize: Color red lighter lighter) 
+                equals: 'Color{#red:1.0,#green:0.061,#blue:0.061,#alpha:1.0}'.
+
+    "Modified: / 20-05-2020 / 13:25:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+testCustomNewline
+	| output |
+	output := String streamContents: [ :out |
+		(STON writer on: out)
+			newLine: String lf;
+			prettyPrint: true;
+			nextPut: #( 1 ) ].
+	self 
+		assert: output 
+		equals: ('[\	1\]' withCRs replaceAll: Character cr with: Character lf)
+!
+
+testDate
+	| date |
+	date := (Date year: 2012 month: 1 day: 1) translateToUTC.
+	self assert: (self serialize: date) equals: 'Date[''2012-01-01Z'']'.
+	date := (Date year: 2012 month: 1 day: 1) translateTo: 1 hour.
+	self assert: (self serialize: date) equals: 'Date[''2012-01-01+01:00'']'.
+!
+
+testDateAndTime
+	| dateAndTime |
+	dateAndTime := DateAndTime year: 2012 month: 1 day: 1 hour: 6 minute: 30 second: 15 offset: 1 hour.
+	self assert: (self serialize: dateAndTime) = 'DateAndTime[''2012-01-01T06:30:15+01:00'']'
+!
+
+testDictionary
+	| collection |
+	collection := STON mapClass new at: 1 put: 1; at: 2 put: 2; yourself.
+	self assert: (self serialize: collection) = '{1:1,2:2}'.
+	self assert: (self serialize: STON mapClass new) = '{}'.
+!
+
+testDictionaryWithComplexKeys
+	| collection |
+	collection := STON mapClass new at: true put: 1; at: #(foo) put: 2; yourself.
+	self assert: (#('{true:1,[#foo]:2}' '{[#foo]:2,true:1}') includes: (self serialize: collection))
+!
+
+testDiskFile
+	self assert: (self serialize: 'foo.txt' asFileReference) equals: 'FILE[''foo.txt'']'.
+	self assert: (self serialize: '/tmp/foo.txt' asFileReference) equals: 'FILE[''/tmp/foo.txt'']'.
+	self assert: (self serialize: 'tmp/foo.txt' asFileReference) equals: 'FILE[''tmp/foo.txt'']'.
+	self assert: (self serialize: '/tmp' asFileReference) equals: 'FILE[''/tmp'']'.
+	self assert: (self serialize: '/tmp/' asFileReference) equals: 'FILE[''/tmp'']'.
+!
+
+testDoubleQuotedString
+	| string |
+	self assert: (self serializeJson: 'foo') = '"foo"'.
+	self assert: (self serializeJson: 'FOO') = '"FOO"'.
+	self assert: (self serializeJson: 'élève en Français') = '"élève en Français"'.
+	string := String withAll: { 
+		$". $'. $\. $/. Character tab. Character cr. Character lf. Character newPage. Character backspace }.
+	"Note that in JSON mode, double quotes get escaped, and single quotes not"
+	self assert: (self serializeJson: string) equals: '"\"''\\/\t\r\n\f\b"'.
+!
+
+testEmptyArrayPretty
+	self assert: (self serializePretty: STON listClass new) equals: '[ ]' 
+!
+
+testEmptyDictionaryPretty
+	self assert: (self serializePretty: STON mapClass new) equals: '{ }' 
+!
+
+testFloat
+	self assert: (self serialize: 1.5) = '1.5'.
+	self assert: (self serialize: 0.0) = '0.0'.
+	self assert: (self serialize: -1.5) = '-1.5'.
+	self assert: ((self serialize: Float pi) beginsWith:  '3.14159').
+	self assert: ((self serialize: (1/3) asFloat) beginsWith:  '0.333').
+	self assert: (self serialize: (10 raisedTo: 100) asFloat) = '1.0e100'.
+	self assert: (self serialize: (10 raisedTo: -50) asFloat) = '1.0e-50'.
+	self assert: (self serialize: (10 raisedTo: -50) asFloat negated) = '-1.0e-50'.
+!
+
+testFraction
+	self assert: (self serialize: 1/3) equals: '1/3'.
+	self assert: (self serialize: -1/3) equals: '-1/3'.
+	self assert: (self serialize: 10/100) equals: '1/10'.
+	self assert: (self serialize: 100/10) equals: '10'.
+	self assert: (self serialize: 123/123) equals: '1'.
+	self assert: (self serialize: 100/11) equals: '100/11'.
+!
+
+testIdentityDictionary
+	| collection |
+	collection := IdentityDictionary new at: 1 put: 1; at: 2 put: 2; yourself.
+	self assert: (self serialize: collection) = 'IdentityDictionary{1:1,2:2}'.
+	self assert: (self serialize: IdentityDictionary new) = 'IdentityDictionary{}'.
+!
+
+testInteger
+	self assert: (self serialize: 1) = '1'.
+	self assert: (self serialize: 0) = '0'.
+	self assert: (self serialize: -1) = '-1'.
+	self assert: (self serialize: 1234567890) = '1234567890'.
+	self assert: (self serialize: -1234567890) = '-1234567890'
+!
+
+testIsSimpleSymbol
+	self assert: (STON writer isSimpleSymbol: #foo).
+	self assert: (STON writer isSimpleSymbol: #az).
+	self assert: (STON writer isSimpleSymbol: #AZ).
+	self assert: (STON writer isSimpleSymbol: #N0123456789).
+	self assert: (STON writer isSimpleSymbol: #foo123).
+	self assert: (STON writer isSimpleSymbol: #'Foo/Bar').
+	self assert: (STON writer isSimpleSymbol: #'Foo.Bar').
+	self assert: (STON writer isSimpleSymbol: #'Foo-Bar').
+	self assert: (STON writer isSimpleSymbol: #'Foo_Bar').
+	self assert: (STON writer isSimpleSymbol: #foo).
+	self deny: (STON writer isSimpleSymbol: #'#^&$%')
+!
+
+testKeepingNewLines
+        | input result output |
+        input := 'line ending with CR', String return, 
+                'line ending with LF', String lf, 
+                'line ending with CRLF', String crlf.
+        output := '''line ending with CR', String crlf, 
+                'line ending with LF', String crlf, 
+                'line ending with CRLF', String crlf, ''''.
+        result := String streamContents: [ :out |
+                (STON writer on: out) newLine: String crlf; keepNewLines: true; nextPut: input ].
+        self assert: result equals: output.
+        output := '''line ending with CR', String return, 
+                'line ending with LF', String return, 
+                'line ending with CRLF', String return, ''''.
+        result := String streamContents: [ :out |
+                (STON writer on: out) newLine: String return; keepNewLines: true; nextPut: input ].
+        self assert: result equals: output
+
+    "Modified: / 04-06-2019 / 10:52:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+testList
+	self assert: (self serialize: (STON listClass withAll: #(1 2 3))) = '[1,2,3]'.
+	self assert: (self serialize: STON listClass new) = '[]'.
+	self assert: (self serialize: (STON listClass withAll: { 1. -1. 0. #foo. 'a b c'. true. false. nil })) = '[1,-1,0,#foo,''a b c'',true,false,nil]'
+!
+
+testMap
+	| map |
+	(map := STON mapClass new)
+		at: #foo put: 1;
+		at: #bar put: 2.
+	self assert: (self serialize: map) = '{#foo:1,#bar:2}'.
+	self assert: (self serialize: STON mapClass new) = '{}'.
+	map removeAll.
+	map at: 'foo bar' put: #ok.
+	self assert: (self serialize: map) = '{''foo bar'':#ok}'.
+	map removeAll.
+	map at: 123 put: 456.
+	self assert: (self serialize: map) = '{123:456}'.
+
+!
+
+testMetaclass
+	self assert: (self serialize: Point class) = 'Metaclass[#Point]'
+!
+
+testMimeType
+	self
+		assert: (self serialize: ZnMimeType applicationJson)
+		equals: 'MimeType[''application/json'']'.
+	self
+		assert: (self serialize: ZnMimeType textPlain)
+		equals: 'MimeType[''text/plain;charset=utf-8'']'.
+!
+
+testNil
+	self assert: (self serialize: nil) = 'nil'
+!
+
+testNonBMPCharacterEncoding
+	"Characters not in the Basic Multilingual Plane are encoded as a UTF-16 surrogate pair"
+
+	| string json |
+	string := String with: 16r1D11E asCharacter. "MUSICAL SYMBOL G CLEF"
+	json := String streamContents: [ :out |
+		(STON writer on: out) asciiOnly: true; nextPut: string ].
+	self assert: json equals: '''\uD834\uDD1E'''
+!
+
+testNull
+	self assert: (self serializeJson: nil) equals: 'null'
+!
+
+testOrderedCollection
+	| collection |
+	collection := OrderedCollection with: 1 with: 2 with: 3.
+	self assert: (self serialize: collection) = 'OrderedCollection[1,2,3]'.
+	self assert: (self serialize: OrderedCollection new) = 'OrderedCollection[]'.
+!
+
+testPoint
+	self assert: (self serialize: 1@2) = 'Point[1,2]'
+!
+
+testReferenceCycle
+	| array |
+	array := STON listClass with: 1 with: nil.
+	array at: 2 put: array.
+	self assert: (self serialize: array) = '[1,@1]'.
+!
+
+testReferenceSharing
+	| array one |
+	one := { #one }.
+	array := STON listClass with: one with: one with: one.
+	self assert: (self serialize: array) = '[[#one],@2,@2]'.
+!
+
+testReferenceSharingError
+	| serializer array one |
+	serializer := [ :object | 
+		String streamContents: [ :stream |
+			STON writer 
+				on: stream;
+				referencePolicy: #error; 
+				nextPut: object ] ].
+	one := { #one }.
+	array := STON listClass with: one with: one with: one.
+	self 
+		should: [ (serializer value: array) = '[[#one],[#one],[#one]]' ] 
+		raise: STONWriterError
+!
+
+testReferenceSharingIgnore
+	| serializer array one |
+	serializer := [ :object | 
+		String streamContents: [ :stream |
+			STON writer 
+				on: stream;
+				referencePolicy: #ignore; 
+				nextPut: object ] ].
+	one := { #one }.
+	array := STON listClass with: one with: one with: one.
+	self assert: (serializer value: array) = '[[#one],[#one],[#one]]'.
+!
+
+testRestrictedClassesInJsonMode
+	self should: [ self serializeJson: 1@2 ] raise: STONWriterError.
+	self should: [ self serializeJson: #foo->100 ] raise: STONWriterError.
+	self should: [ self serializeJson: STONTestUser dummy ] raise: STONWriterError.
+!
+
+testScaledDecimal
+	self assert: (self serialize: 1/3s2) equals: '1/3s2'.
+	self assert: (self serialize: -1/3s2) equals: '-1/3s2'.
+	self assert: (self serialize: 1/3s10) equals: '1/3s10'.
+	self assert: (self serialize: -1/3s10) equals: '-1/3s10'.
+!
+
+testString
+	| string |
+	self assert: (self serialize: 'foo') = '''foo'''.
+	self assert: (self serialize: 'FOO') = '''FOO'''.
+	self assert: (self serializeAsciiOnly: 'élève en Français') = '''\u00E9l\u00E8ve en Fran\u00E7ais'''.
+	self assert: (self serialize: 'élève en Français') = '''élève en Français'''.
+	string := String withAll: { 
+		$". $'. $\. $/. Character tab. Character cr. Character lf.  Character newPage. Character backspace }.
+	self assert: (self serialize: string) equals: '''"\''\\/\t\r\n\f\b'''.
+!
+
+testSymbol
+	self assert: (self serialize: #foo) = '#foo'.
+	self assert: (self serialize: #FOO) = '#FOO'.
+	self assert: (self serialize: #bytes) = '#bytes'.
+	self assert: (self serialize: #'foo.bar') = '#foo.bar'.
+	self assert: (self serialize: #'foo-bar') = '#foo-bar'.
+	self assert: (self serialize: #'foo_bar') = '#foo_bar'.
+	self assert: (self serialize: #'foo/bar') = '#foo/bar'.
+	self assert: (self serialize: #'foo bar') = '#''foo bar'''.
+	self assert: (self serialize: #foo123) = '#foo123'.
+!
+
+testSymbolAsString
+	self assert: (self serializeJson: #foo) = '"foo"'.
+	self assert: (self serializeJson: #'FOO') = '"FOO"'.
+!
+
+testTime
+	| time |
+	time := Time hour: 6 minute: 30 second: 15.
+	self assert: (self serialize: time) equals: 'Time[''06:30:15'']'.
+	time := Time hour: 6 minute: 30 second: 15 nanoSecond: 123.
+	self assert: (self serialize: time) equals: 'Time[''06:30:15.000000123'']'.
+!
+
+testURL
+	self
+		assert: (self serialize: 'https://pharo.org/files/pharo.png' asUrl)
+		equals: 'URL[''https://pharo.org/files/pharo.png'']'.
+	self
+		assert: (self serialize: 'mailto:sven@stfx.eu' asUrl)
+		equals: 'URL[''mailto:sven@stfx.eu'']'.
+	self
+		assert: (self serialize: 'file:///var/log/system.log' asUrl)
+		equals: 'URL[''file:///var/log/system.log'']'.
+	self
+		assert: (self serialize: 'scheme://user:password@host:123/var/log/system.log?foo=1&bar#frag' asUrl)
+		equals: 'URL[''scheme://user:password@host:123/var/log/system.log?foo=1&bar#frag'']'.
+!
+
+testUser
+	| user |
+	(user := STONTestUser new)
+		username: 'john@foo.com';
+		password: 'secret1'.
+	self 
+		assert: (self serialize: user)
+		equals: 'TestUser{#username:''john@foo.com'',#password:''secret1'',#enabled:true}'
+!
+
+testUser2
+	| user |
+	(user := STONTestUser2 new)
+		username: 'john@foo.com';
+		password: 'secret1'.
+	self 
+		assert: (self serialize: user)
+		equals: 'TestUser2{#username:''john@foo.com'',#password:''secret1'',#enabled:true}'
+!
+
+testUser3Nil
+	| user |
+	user := STONTestUser3 new.
+	self 
+		assert: (self serialize: user) 
+		equals: 'TestUser3{#username:nil,#password:nil,#enabled:true}'
+!
+
+testUserNil
+	| user |
+	user := STONTestUser new.
+	self assert: (self serialize: user) equals: 'TestUser{#enabled:true}'
+! !
+