@@ -66,23 +66,29 @@ extension Snapshotting where Format == String {
6666 }
6767}
6868
69- private func snap< T> ( _ value: T , name: String ? = nil , indent: Int = 0 ) -> String {
69+ private func snap< T> (
70+ _ value: T ,
71+ name: String ? = nil ,
72+ indent: Int = 0 ,
73+ visitedValues: Set < ObjectIdentifier > = . init( )
74+ ) -> String {
7075 let indentation = String ( repeating: " " , count: indent)
7176 let mirror = Mirror ( reflecting: value)
7277 var children = mirror. children
7378 let count = children. count
7479 let bullet = count == 0 ? " - " : " ▿ "
80+ var visitedValues = visitedValues
7581
7682 let description : String
7783 switch ( value, mirror. displayStyle) {
7884 case ( _, . collection? ) :
7985 description = count == 1 ? " 1 element " : " \( count) elements "
8086 case ( _, . dictionary? ) :
8187 description = count == 1 ? " 1 key/value pair " : " \( count) key/value pairs "
82- children = sort ( children)
88+ children = sort ( children, visitedValues : visitedValues )
8389 case ( _, . set? ) :
8490 description = count == 1 ? " 1 member " : " \( count) members "
85- children = sort ( children)
91+ children = sort ( children, visitedValues : visitedValues )
8692 case ( _, . tuple? ) :
8793 description = count == 1 ? " (1 element) " : " ( \( count) elements) "
8894 case ( _, . optional? ) :
@@ -95,10 +101,19 @@ private func snap<T>(_ value: T, name: String? = nil, indent: Int = 0) -> String
95101 return " \( indentation) - \( name. map { " \( $0) : " } ?? " " ) \( value. snapshotDescription) \n "
96102 case ( let value as CustomStringConvertible , _) :
97103 description = value. description
98- case ( _, . class? ) , ( _, . struct? ) :
104+ case let ( value as AnyObject , . class? ) :
105+ let objectID = ObjectIdentifier ( value)
106+ if visitedValues. contains ( objectID) {
107+ return " \( indentation) \( bullet) \( name ?? " value " ) (circular reference detected) \n "
108+ }
109+ visitedValues. insert ( objectID)
110+ description = String ( describing: mirror. subjectType)
111+ . replacingOccurrences ( of: " # \\ d+ " , with: " " , options: . regularExpression)
112+ children = sort ( children, visitedValues: visitedValues)
113+ case ( _, . struct? ) :
99114 description = String ( describing: mirror. subjectType)
100115 . replacingOccurrences ( of: " # \\ d+ " , with: " " , options: . regularExpression)
101- children = sort ( children)
116+ children = sort ( children, visitedValues : visitedValues )
102117 case ( _, . enum? ) :
103118 let subjectType = String ( describing: mirror. subjectType)
104119 . replacingOccurrences ( of: " # \\ d+ " , with: " " , options: . regularExpression)
@@ -109,15 +124,15 @@ private func snap<T>(_ value: T, name: String? = nil, indent: Int = 0) -> String
109124
110125 let lines =
111126 [ " \( indentation) \( bullet) \( name. map { " \( $0) : " } ?? " " ) \( description) \n " ]
112- + children. map { snap ( $1, name: $0, indent: indent + 2 ) }
127+ + children. map { snap ( $1, name: $0, indent: indent + 2 , visitedValues : visitedValues ) }
113128
114129 return lines. joined ( )
115130}
116131
117- private func sort( _ children: Mirror . Children ) -> Mirror . Children {
132+ private func sort( _ children: Mirror . Children , visitedValues : Set < ObjectIdentifier > ) -> Mirror . Children {
118133 return . init(
119134 children
120- . map ( { ( child: $0, snap: snap ( $0) ) } )
135+ . map ( { ( child: $0, snap: snap ( $0, visitedValues : visitedValues ) ) } )
121136 . sorted ( by: { $0. snap < $1. snap } )
122137 . map ( { $0. child } )
123138 )
0 commit comments