@@ -124,6 +124,43 @@ fn names() -> World {
124124 }
125125}
126126
127+ /// A column that orders numbers of both signs and many magnitudes, so the
128+ /// terms' shared prefixes (sign, then exponent) have something to show; two
129+ /// integers past 2^53 that round to one `f64`, so one term under two value
130+ /// selectors; and leaves at the ordered path that carry no term (a null, a
131+ /// boolean, an empty object, an array, which holds its own leaf below the
132+ /// ordered path).
133+ fn numbers ( ) -> World {
134+ World {
135+ name : "ordered numbers" ,
136+ column : Column {
137+ sealed_root : true ,
138+ ordered : ordered ( & [ "$.v" ] ) ,
139+ } ,
140+ docs : vec ! [
141+ json!( { "v" : 1 } ) ,
142+ json!( { "v" : -2 } ) ,
143+ json!( { "v" : 3 } ) ,
144+ json!( { "v" : 9_007_199_254_740_993u64 } ) ,
145+ json!( { "v" : 9_007_199_254_740_992u64 } ) ,
146+ json!( { "v" : 2.5 } ) ,
147+ json!( { "v" : 1e300 } ) ,
148+ json!( { "v" : -1e300 } ) ,
149+ json!( { "v" : 0.001 } ) ,
150+ json!( { "v" : null} ) ,
151+ json!( { "v" : true } ) ,
152+ json!( { "v" : { } } ) ,
153+ json!( { "v" : [ 3 ] } ) ,
154+ ] ,
155+ queries : vec ! [
156+ Query :: Order ( "$.v" . into( ) , json!( 0 ) ) ,
157+ Query :: Order ( "$.v" . into( ) , json!( 9_007_199_254_740_992u64 ) ) ,
158+ Query :: Order ( "$.v" . into( ) , json!( -1 ) ) ,
159+ Query :: subtree( "$.v" ) ,
160+ ] ,
161+ }
162+ }
163+
127164/// A column with three wide top-level objects, each read whole, so that
128165/// whether members' ordinals follow their keys' order is decidable rather
129166/// than a coin toss (see `distinguishers::member_ordinals_follow_key_order`).
@@ -158,8 +195,8 @@ fn wide() -> World {
158195 }
159196}
160197
161- /// The handcrafted worlds: the shapes under each root option, and a column
162- /// of ordered strings .
198+ /// The handcrafted worlds: the shapes under each root option, a column of
199+ /// ordered strings, one of ordered numbers, and one of wide objects .
163200pub fn handcrafted ( ) -> Vec < World > {
164201 vec ! [
165202 World {
@@ -181,6 +218,7 @@ pub fn handcrafted() -> Vec<World> {
181218 queries: shape_queries( false ) ,
182219 } ,
183220 names( ) ,
221+ numbers( ) ,
184222 wide( ) ,
185223 ]
186224}
@@ -191,12 +229,26 @@ pub fn handcrafted() -> Vec<World> {
191229
192230const KEYS : & [ & str ] = & [ "a" , "b" , "c" , "list" , "n" , "name" , "tags" , "zz" ] ;
193231
232+ /// The strings the random columns hold: short, sharing prefixes, and some
233+ /// that fold to one another (`"X"` and `"x"`, `"é"` and `"e"`) or are wider
234+ /// in bytes than in characters.
235+ const STRINGS : & [ & str ] = & [ "x" , "yy" , "x" , "zzz" , "ab" , "" , "X" , "é" , "e" , "Ab" ] ;
236+
194237fn leaf ( ) -> impl Strategy < Value = Value > {
195238 prop_oneof ! [
196- Just ( Value :: Null ) ,
197- any:: <bool >( ) . prop_map( Value :: Bool ) ,
198- ( -3i64 ..6 ) . prop_map( Value :: from) ,
199- prop:: sample:: select( vec![ "x" , "yy" , "x" , "zzz" , "ab" , "" ] ) . prop_map( Value :: from) ,
239+ 1 => Just ( Value :: Null ) ,
240+ 1 => any:: <bool >( ) . prop_map( Value :: Bool ) ,
241+ 4 => ( -3i64 ..6 ) . prop_map( Value :: from) ,
242+ // Past 2^53, where two integers can round to one f64 and so share
243+ // an ordering term while their value selectors differ; and floats.
244+ 1 => prop:: sample:: select( vec![
245+ json!( 9_007_199_254_740_992u64 ) ,
246+ json!( 9_007_199_254_740_993u64 ) ,
247+ json!( 2.5 ) ,
248+ json!( -0.5 ) ,
249+ json!( 1e300 ) ,
250+ ] ) ,
251+ 4 => prop:: sample:: select( STRINGS ) . prop_map( Value :: from) ,
200252 ]
201253}
202254
@@ -223,37 +275,92 @@ fn value() -> impl Strategy<Value = Value> {
223275 } )
224276}
225277
226- /// Every value under the key `n` made a number, since the random columns
227- /// order `$.n` and `$.list[*].n` and a world orders one kind of leaf.
228- fn numberize ( value : Value ) -> Value {
229- match value {
230- Value :: Object ( map) => Value :: Object (
231- map. into_iter ( )
232- . map ( |( k, v) | {
233- let v = if k == "n" && !v. is_number ( ) {
234- Value :: from ( i64:: try_from ( v. to_string ( ) . len ( ) % 7 ) . unwrap ( ) )
235- } else {
236- numberize ( v)
237- } ;
238- ( k, v)
278+ /// Which kind of leaf a random column orders: a world orders one (see
279+ /// [`super::profile::Ids::ordering_ranks`]).
280+ #[ derive( Clone , Copy , Debug ) ]
281+ enum Ordered {
282+ /// `$.n` and `$.list[*].n`.
283+ Numbers ,
284+ /// `$.name` and `$.tags[*]`.
285+ Strings ,
286+ }
287+
288+ impl Ordered {
289+ fn paths ( self ) -> Vec < JsonPath > {
290+ match self {
291+ Self :: Numbers => ordered ( & [ "$.n" , "$.list[*].n" ] ) ,
292+ Self :: Strings => ordered ( & [ "$.name" , "$.tags[*]" ] ) ,
293+ }
294+ }
295+
296+ /// `value` with every leaf an ordered path could reach made the kind
297+ /// this column orders: a string under the key `n` a number, or a number
298+ /// under the key `name` or directly in an array under `tags` a string.
299+ /// Everything else stays, `null`s, booleans and containers at the
300+ /// ordered paths included, which carry no term.
301+ fn fix ( self , value : Value ) -> Value {
302+ let to_number = |v : & Value | Value :: from ( i64:: try_from ( v. to_string ( ) . len ( ) % 7 ) . unwrap ( ) ) ;
303+ let to_string = |v : & Value | Value :: from ( STRINGS [ v. to_string ( ) . len ( ) % STRINGS . len ( ) ] ) ;
304+ match ( self , value) {
305+ ( _, Value :: Object ( map) ) => Value :: Object (
306+ map. into_iter ( )
307+ . map ( |( k, v) | {
308+ let v = match ( self , k. as_str ( ) , v) {
309+ ( Self :: Numbers , "n" , v) if v. is_string ( ) => to_number ( & v) ,
310+ ( Self :: Strings , "name" , v) if v. is_number ( ) => to_string ( & v) ,
311+ ( Self :: Strings , "tags" , Value :: Array ( items) ) => Value :: Array (
312+ items
313+ . into_iter ( )
314+ . map ( |item| {
315+ if item. is_number ( ) {
316+ to_string ( & item)
317+ } else {
318+ self . fix ( item)
319+ }
320+ } )
321+ . collect ( ) ,
322+ ) ,
323+ ( _, _, v) => self . fix ( v) ,
324+ } ;
325+ ( k, v)
326+ } )
327+ . collect ( ) ,
328+ ) ,
329+ ( _, Value :: Array ( items) ) => {
330+ Value :: Array ( items. into_iter ( ) . map ( |item| self . fix ( item) ) . collect ( ) )
331+ }
332+ ( _, other) => other,
333+ }
334+ }
335+
336+ /// An ordering bound of this kind at one of this kind's paths.
337+ fn query ( self ) -> BoxedStrategy < Query > {
338+ match self {
339+ Self :: Numbers => ( prop:: sample:: select ( vec ! [ "$.n" , "$.list[*].n" ] ) , leaf ( ) )
340+ . prop_filter_map ( "a number bound" , |( p, v) | {
341+ v. is_number ( ) . then ( || Query :: Order ( p. into ( ) , v) )
239342 } )
240- . collect ( ) ,
241- ) ,
242- Value :: Array ( items) => Value :: Array ( items. into_iter ( ) . map ( numberize) . collect ( ) ) ,
243- other => other,
343+ . boxed ( ) ,
344+ Self :: Strings => (
345+ prop:: sample:: select ( vec ! [ "$.name" , "$.tags[*]" ] ) ,
346+ prop:: sample:: select ( STRINGS ) ,
347+ )
348+ . prop_map ( |( p, v) | Query :: Order ( p. into ( ) , Value :: from ( v) ) )
349+ . boxed ( ) ,
350+ }
244351 }
245352}
246353
247- fn document ( ) -> impl Strategy < Value = Value > {
354+ fn document ( kind : Ordered ) -> impl Strategy < Value = Value > {
248355 prop_oneof ! [
249356 8 => object( value( ) , 0 ..5 ) ,
250357 1 => prop:: collection:: vec( value( ) , 0 ..3 ) . prop_map( Value :: Array ) ,
251358 1 => leaf( ) ,
252359 ]
253- . prop_map ( numberize )
360+ . prop_map ( move |v| kind . fix ( v ) )
254361}
255362
256- fn query ( ) -> impl Strategy < Value = Query > {
363+ fn query ( kind : Ordered ) -> impl Strategy < Value = Query > {
257364 let subtree = vec ! [
258365 "$" ,
259366 "$.a" ,
@@ -294,31 +401,34 @@ fn query() -> impl Strategy<Value = Query> {
294401 prop:: collection:: vec( leaf( ) , 1 ..3 ) . prop_map( Value :: Array ) ,
295402 object( prop:: collection:: vec( object( leaf( ) , 1 ..2 ) , 1 ..2 ) . prop_map( Value :: Array ) , 1 ..2 ) ,
296403 ]
297- . prop_map( |v| Query :: Contains ( numberize ( v) ) ) ,
404+ . prop_map( move |v| Query :: Contains ( kind . fix ( v) ) ) ,
298405 1 => ( prop:: sample:: select( vec![ "$.a" , "$.tags[0]" , "$.list[*].b" ] ) ,
299406 prop_oneof![ ( -3i64 ..6 ) . prop_map( Value :: from) , prop:: sample:: select( vec![ "x" , "ab" ] ) . prop_map( Value :: from) ] )
300407 . prop_map( |( p, v) | Query :: Equals ( p. into( ) , v) ) ,
301- 1 => ( prop:: sample:: select( vec![ "$.n" , "$.list[*].n" ] ) , -3i64 ..6 )
302- . prop_map( |( p, v) | Query :: Order ( p. into( ) , Value :: from( v) ) ) ,
408+ 1 => kind. query( ) ,
303409 ]
304410}
305411
306- /// A random column: a root option, two to six documents, and four to ten
307- /// queries, some of which repeat.
412+ /// A random column: a root option, the kind of leaf it orders, two to six
413+ /// documents, and four to ten queries, some of which repeat.
308414pub fn random_world ( ) -> impl Strategy < Value = World > {
309- any :: < bool > ( ) . prop_flat_map ( |sealed_root| {
310- (
311- prop:: collection:: vec ( document ( ) , 2 ..7 ) ,
312- prop:: collection:: vec ( query ( ) , 4 ..11 ) ,
313- )
314- . prop_map ( move |( docs, queries) | World {
315- name : "random" ,
316- column : Column {
317- sealed_root,
318- ordered : ordered ( & [ "$.n" , "$.list[*].n" ] ) ,
319- } ,
320- docs,
321- queries,
322- } )
323- } )
415+ (
416+ any :: < bool > ( ) ,
417+ prop_oneof ! [ Just ( Ordered :: Numbers ) , Just ( Ordered :: Strings ) ] ,
418+ )
419+ . prop_flat_map ( |( sealed_root, kind) | {
420+ (
421+ prop:: collection:: vec ( document ( kind) , 2 ..7 ) ,
422+ prop:: collection:: vec ( query ( kind) , 4 ..11 ) ,
423+ )
424+ . prop_map ( move |( docs, queries) | World {
425+ name : "random" ,
426+ column : Column {
427+ sealed_root,
428+ ordered : kind. paths ( ) ,
429+ } ,
430+ docs,
431+ queries,
432+ } )
433+ } )
324434}
0 commit comments