@@ -13,28 +13,35 @@ where
1313{
1414 let model = get_list_store ( tree_view) ;
1515
16- if let Some ( curr_iter) = model. iter_first ( ) {
17- assert ! ( model. get:: <bool >( & curr_iter, column_header) ) ; // First item should be header
18- assert ! ( model. iter_next( & curr_iter) ) ; // Must be at least two items
16+ if let Some ( mut curr_iter) = model. iter_first ( ) {
17+ assert ! ( model. get:: <bool >( & curr_iter, column_header) ) ;
18+ assert ! ( model. iter_next( & curr_iter) ) ;
1919 loop {
2020 let mut iters = Vec :: new ( ) ;
2121 let mut all_have = false ;
22+ let local_iter = curr_iter;
2223 loop {
23- if model. get :: < bool > ( & curr_iter, column_header) {
24- assert ! ( model. iter_next( & curr_iter) , "Empty header, this should not happens" ) ;
24+ if model. get :: < bool > ( & local_iter, column_header) {
25+ if !model. iter_next ( & local_iter) {
26+ all_have = true ;
27+ }
2528 break ;
2629 }
27- iters. push ( curr_iter ) ;
28- if !model. iter_next ( & curr_iter ) {
30+ iters. push ( local_iter ) ;
31+ if !model. iter_next ( & local_iter ) {
2932 all_have = true ;
3033 break ;
3134 }
3235 }
3336 if iters. len ( ) == 1 {
34- continue ; // Can be equal 1 in reference folders
37+ curr_iter = local_iter;
38+ if all_have {
39+ break ;
40+ }
41+ continue ;
3542 }
36-
3743 sort_iters :: < T > ( & model, iters, column_sort) ;
44+ curr_iter = local_iter;
3845 if all_have {
3946 break ;
4047 }
@@ -142,6 +149,7 @@ mod test {
142149 use glib:: types:: Type ;
143150 use gtk4:: prelude:: * ;
144151 use gtk4:: { Popover , TreeView } ;
152+ use rand:: random;
145153
146154 use crate :: connect_things:: connect_popovers_sort:: { popover_sort_general, sort_iters} ;
147155
@@ -231,58 +239,60 @@ mod test {
231239 }
232240 }
233241
234- // TODO - This test uncovers a bug in the code, so it is disabled for now, with sort button
235- // #[gtk4::test]
236- // pub(crate) fn _fuzzer_test() {
237- // for _ in 0..10000 {
238- // let columns_types: &[Type] = &[Type::BOOL, Type::STRING];
239- // let list_store = gtk4::ListStore::new(columns_types);
240- // let tree_view = TreeView::builder().model(&list_store).build();
241- // let popover = Popover::new();
242- //
243- // let first_row: &[(u32, &dyn ToValue)] = &[(0, &true), (1, &"AAA")];
244- // list_store.set(&list_store.append(), first_row);
245- //
246- // let mut since_last_header = 0;
247- //
248- // (0..(random::<u32>() % 10 + 5)).for_each(|_| {
249- // let bool_val = if since_last_header < 2 {
250- // since_last_header += 1;
251- // false
252- // } else {
253- // since_last_header = 0;
254- // random()
255- // };
256- // let string_val = rand::random::<u32>().to_string();
257- // let a: Vec<(u32, &dyn ToValue)> = vec![(0, &bool_val), (1, &string_val)];
258- //
259- // list_store.set(&list_store.append(), &a);
260- // });
261- //
262- // if since_last_header < 2 {
263- // // This is invalid, and should be vec![(0, &false), (1, &"AAA")]
264- // // but this triggers the bug
265- // let a: Vec<(u32, &dyn ToValue)> = vec![(0, &true), (1, &"AAA")];
266- // list_store.set(&list_store.append(), &a);
267- // let b: Vec<(u32, &dyn ToValue)> = vec![(0, &false), (1, &"BBB")];
268- // list_store.set(&list_store.append(), &b);
269- // }
270- //
271- // print_two_items_model(&list_store);
272- //
273- // popover_sort_general::<String>(&popover, &tree_view, 1, 0);
274- // }
275- // }
276- //
277- // fn print_two_items_model(model: >k4::ListStore) {
278- // let iter = model.iter_first().expect("Failed to get first iter");
279- // loop {
280- // let bool_val = model.get::<bool>(&iter, 0);
281- // let string_val = model.get::<String>(&iter, 1);
282- // println!("{bool_val} {string_val}");
283- // if !model.iter_next(&iter) {
284- // break;
285- // }
286- // }
287- // }
242+ #[ gtk4:: test]
243+ pub ( crate ) fn fuzzer_test ( ) {
244+ for _ in 0 ..100000 {
245+ let columns_types: & [ Type ] = & [ Type :: BOOL , Type :: STRING ] ;
246+ let list_store = gtk4:: ListStore :: new ( columns_types) ;
247+ let tree_view = TreeView :: builder ( ) . model ( & list_store) . build ( ) ;
248+ let popover = Popover :: new ( ) ;
249+
250+ // Always start with a header
251+ let first_row: & [ ( u32 , & dyn ToValue ) ] = & [ ( 0 , & true ) , ( 1 , & "AAA" ) ] ;
252+ list_store. set ( & list_store. append ( ) , first_row) ;
253+
254+ let mut since_last_header = 0 ;
255+ let mut need_header = false ;
256+ let num_rows = ( random :: < u32 > ( ) % 10 + 5 ) as usize ;
257+ let mut i = 0 ;
258+ while i < num_rows {
259+ if need_header {
260+ // Insert a header only if last was not a header
261+ let a: Vec < ( u32 , & dyn ToValue ) > = vec ! [ ( 0 , & true ) , ( 1 , & "HEADER" ) ] ;
262+ list_store. set ( & list_store. append ( ) , & a) ;
263+ since_last_header = 0 ;
264+ need_header = false ;
265+ i += 1 ;
266+ continue ;
267+ }
268+ // Insert a non-header row
269+ let string_val = rand:: random :: < u32 > ( ) . to_string ( ) ;
270+ let a: Vec < ( u32 , & dyn ToValue ) > = vec ! [ ( 0 , & false ) , ( 1 , & string_val) ] ;
271+ list_store. set ( & list_store. append ( ) , & a) ;
272+ since_last_header += 1 ;
273+ // After at least 2 non-header rows, randomly decide to insert a header next
274+ if since_last_header >= 2 && random :: < u8 > ( ) % 3 == 0 {
275+ need_header = true ;
276+ }
277+ i += 1 ;
278+ }
279+
280+ // Ensure at least one non-header after the last header
281+ let last_iter = list_store. iter_first ( ) . unwrap ( ) ;
282+ let mut last_is_header;
283+ loop {
284+ last_is_header = list_store. get :: < bool > ( & last_iter, 0 ) ;
285+ if !list_store. iter_next ( & last_iter) {
286+ break ;
287+ }
288+ }
289+ if last_is_header {
290+ let a: Vec < ( u32 , & dyn ToValue ) > = vec ! [ ( 0 , & false ) , ( 1 , & "FINALROW" ) ] ;
291+ list_store. set ( & list_store. append ( ) , & a) ;
292+ }
293+
294+ // Optionally: print_two_items_model(&list_store);
295+ popover_sort_general :: < String > ( & popover, & tree_view, 1 , 0 ) ;
296+ }
297+ }
288298}
0 commit comments