@@ -10,15 +10,14 @@ use ic_management_canister_types_private::{
1010 CanisterStatusType , EcdsaKeyId , EmptyBlob , Method , Payload as _, SchnorrKeyId ,
1111} ;
1212use ic_registry_subnet_type:: SubnetType ;
13- use ic_replicated_state:: testing:: SystemStateTesting ;
1413use ic_replicated_state:: { CanisterStatus , metadata_state:: testing:: NetworkTopologyTesting } ;
1514use ic_state_machine_tests:: { PayloadBuilder , StateMachineBuilder } ;
1615use ic_test_utilities_metrics:: { fetch_counter, fetch_histogram_vec_buckets} ;
1716use ic_test_utilities_state:: get_running_canister;
1817use ic_test_utilities_types:: messages:: RequestBuilder ;
18+ use ic_types:: Cycles ;
1919use ic_types:: messages:: { CallbackId , Payload , RejectContext } ;
2020use ic_types:: time:: { UNIX_EPOCH , expiry_time_from_now} ;
21- use ic_types:: { ComputeAllocation , Cycles } ;
2221use ic_types_test_utils:: ids:: { canister_test_id, message_test_id, subnet_test_id, user_test_id} ;
2322use ic00:: { CanisterHttpRequestArgs , HttpMethod } ;
2423use proptest:: prelude:: * ;
@@ -443,182 +442,101 @@ fn can_timeout_stop_canister_requests() {
443442}
444443
445444#[ test]
446- fn test_is_next_method_added_to_task_queue ( ) {
447- let mut test = SchedulerTestBuilder :: new ( ) . build ( ) ;
448-
449- let canister = test. create_canister_with (
450- Cycles :: new ( 1_000_000_000_000 ) ,
451- ComputeAllocation :: zero ( ) ,
452- MemoryAllocation :: default ( ) ,
453- None ,
454- None ,
455- None ,
456- ) ;
457- let has_heartbeat = false ;
458- let has_active_timer = false ;
459-
460- let mut heartbeat_and_timer_canisters = BTreeSet :: new ( ) ;
461- assert ! (
462- !test
463- . canister_state( canister)
464- . system_state
465- . queues( )
466- . has_input( )
467- ) ;
445+ fn test_maybe_add_heartbeat_or_global_timer_tasks ( ) {
446+ use ExecutionTask as Task ;
447+ use NextScheduledMethod :: * ;
448+
449+ /// Calls `maybe_add_heartbeat_or_global_timer_tasks` in the given state and
450+ /// returns the tasks that were enqueued and the next scheduled method.
451+ fn call (
452+ has_input : bool ,
453+ has_heartbeat : bool ,
454+ has_active_timer : bool ,
455+ next_scheduled_method : NextScheduledMethod ,
456+ ) -> ( Vec < ExecutionTask > , NextScheduledMethod ) {
457+ let mut test = SchedulerTestBuilder :: new ( ) . build ( ) ;
458+ let canister_id = test. create_canister ( ) ;
459+ let canister = test. canister_state_mut ( canister_id) ;
460+
461+ if has_input {
462+ canister. push_ingress ( Ingress {
463+ source : user_test_id ( 77 ) ,
464+ receiver : canister_id,
465+ effective_canister_id : None ,
466+ method_name : String :: from ( "test" ) ,
467+ method_payload : vec ! [ 1_u8 ] ,
468+ message_id : message_test_id ( 555 ) ,
469+ expiry_time : expiry_time_from_now ( ) ,
470+ } ) ;
471+ }
472+ while canister. get_next_scheduled_method ( ) != next_scheduled_method {
473+ canister. inc_next_scheduled_method ( ) ;
474+ }
468475
469- for _ in 0 ..3 {
470- // The timer did not reach the deadline and the canister does not have
471- // input, hence no method will be chosen.
472- assert ! ( !is_next_method_chosen(
473- test. canister_state_mut( canister) ,
476+ let mut heartbeat_and_timer_canisters = BTreeSet :: new ( ) ;
477+ maybe_add_heartbeat_or_global_timer_tasks (
478+ canister,
474479 has_heartbeat,
475480 has_active_timer,
476481 & mut heartbeat_and_timer_canisters,
477- ) ) ;
478- assert_eq ! ( heartbeat_and_timer_canisters, BTreeSet :: new( ) ) ;
479- test. canister_state_mut ( canister)
480- . inc_next_scheduled_method ( ) ;
481- }
482-
483- // Make canister able to schedule both heartbeat and global timer.
484- let has_heartbeat = true ;
485- let has_active_timer = true ;
486-
487- // Set input.
488- test. canister_state_mut ( canister)
489- . system_state
490- . queues_mut ( )
491- . push_ingress ( Ingress {
492- source : user_test_id ( 77 ) ,
493- receiver : canister,
494- effective_canister_id : None ,
495- method_name : String :: from ( "test" ) ,
496- method_payload : vec ! [ 1_u8 ] ,
497- message_id : message_test_id ( 555 ) ,
498- expiry_time : expiry_time_from_now ( ) ,
499- } ) ;
500-
501- assert ! (
502- test. canister_state( canister)
503- . system_state
504- . queues( )
505- . has_input( )
506- ) ;
507-
508- while test. canister_state ( canister) . get_next_scheduled_method ( ) != NextScheduledMethod :: Message
509- {
510- test. canister_state_mut ( canister)
511- . inc_next_scheduled_method ( ) ;
512- }
482+ ) ;
513483
514- assert ! ( is_next_method_chosen(
515- test. canister_state_mut( canister) ,
516- has_heartbeat,
517- has_active_timer,
518- & mut heartbeat_and_timer_canisters,
519- ) ) ;
520-
521- // Since NextScheduledMethod is Message it is not expected that Heartbeat
522- // and GlobalTimer are added to the queue.
523- assert ! (
524- test. canister_state( canister)
525- . system_state
526- . task_queue
527- . is_empty( )
528- ) ;
484+ let mut tasks = Vec :: new ( ) ;
485+ while let Some ( task) = canister. system_state . task_queue . pop_front ( ) {
486+ tasks. push ( task) ;
487+ }
529488
530- assert_eq ! ( heartbeat_and_timer_canisters, BTreeSet :: new( ) ) ;
489+ // Iff a task was enqueued, the canister was added to the set.
490+ assert ! (
491+ tasks. is_empty( ) || heartbeat_and_timer_canisters. contains( & canister_id) ,
492+ "tasks: {tasks:?}, heartbeat_and_timer_canisters: {heartbeat_and_timer_canisters:?}"
493+ ) ;
531494
532- while test. canister_state ( canister) . get_next_scheduled_method ( )
533- != NextScheduledMethod :: Heartbeat
534- {
535- test. canister_state_mut ( canister)
536- . inc_next_scheduled_method ( ) ;
495+ ( tasks, canister. get_next_scheduled_method ( ) )
537496 }
538497
539- // Since NextScheduledMethod is Heartbeat it is expected that Heartbeat
540- // and GlobalTimer are added at the front of the queue.
541- assert ! ( is_next_method_chosen(
542- test. canister_state_mut( canister) ,
543- has_heartbeat,
544- has_active_timer,
545- & mut heartbeat_and_timer_canisters,
546- ) ) ;
498+ fn inc ( mut method : NextScheduledMethod ) -> NextScheduledMethod {
499+ method. inc ( ) ;
500+ method
501+ }
547502
548- assert_eq ! ( heartbeat_and_timer_canisters, BTreeSet :: from( [ canister] ) ) ;
503+ // With no input, no heartbeat, no active timer, nothing changes.
504+ assert_eq ! ( call( false , false , false , Message ) , ( vec![ ] , Message ) ) ;
505+ assert_eq ! ( call( false , false , false , Heartbeat ) , ( vec![ ] , Heartbeat ) ) ;
549506 assert_eq ! (
550- test. canister_state( canister)
551- . system_state
552- . task_queue
553- . front( ) ,
554- Some ( & ExecutionTask :: Heartbeat )
507+ call( false , false , false , GlobalTimer ) ,
508+ ( vec![ ] , GlobalTimer )
555509 ) ;
556510
557- test. canister_state_mut ( canister)
558- . system_state
559- . task_queue
560- . pop_front ( ) ;
561-
511+ // With an input but no heartbeat or timer, next method advances past Message.
512+ assert_eq ! ( call( true , false , false , Message ) , ( vec![ ] , inc( Message ) ) ) ;
513+ assert_eq ! ( call( true , false , false , Heartbeat ) , ( vec![ ] , inc( Message ) ) ) ;
562514 assert_eq ! (
563- test. canister_state( canister)
564- . system_state
565- . task_queue
566- . front( ) ,
567- Some ( & ExecutionTask :: GlobalTimer )
515+ call( true , false , false , GlobalTimer ) ,
516+ ( vec![ ] , inc( Message ) )
568517 ) ;
569518
570- test. canister_state_mut ( canister)
571- . system_state
572- . task_queue
573- . pop_front ( ) ;
574-
575- assert_eq ! ( heartbeat_and_timer_canisters, BTreeSet :: from( [ canister] ) ) ;
576-
577- heartbeat_and_timer_canisters = BTreeSet :: new ( ) ;
578-
579- while test. canister_state ( canister) . get_next_scheduled_method ( )
580- != NextScheduledMethod :: GlobalTimer
581- {
582- test. canister_state_mut ( canister)
583- . inc_next_scheduled_method ( ) ;
584- }
585- // Since NextScheduledMethod is GlobalTimer it is expected that GlobalTimer
586- // and Heartbeat are added at the front of the queue.
587- assert ! ( is_next_method_chosen(
588- test. canister_state_mut( canister) ,
589- has_heartbeat,
590- has_active_timer,
591- & mut heartbeat_and_timer_canisters,
592- ) ) ;
593-
594- assert_eq ! ( heartbeat_and_timer_canisters, BTreeSet :: from( [ canister] ) ) ;
519+ // With an input and a heartbeat or timer, it depends on the next method.
520+ assert_eq ! ( call( true , true , false , Message ) , ( vec![ ] , inc( Message ) ) ) ;
595521 assert_eq ! (
596- test. canister_state( canister)
597- . system_state
598- . task_queue
599- . front( ) ,
600- Some ( & ExecutionTask :: GlobalTimer )
522+ call( true , true , false , Heartbeat ) ,
523+ ( vec![ Task :: Heartbeat ] , inc( Heartbeat ) )
601524 ) ;
602-
603- test. canister_state_mut ( canister)
604- . system_state
605- . task_queue
606- . pop_front ( ) ;
607-
608525 assert_eq ! (
609- test. canister_state( canister)
610- . system_state
611- . task_queue
612- . front( ) ,
613- Some ( & ExecutionTask :: Heartbeat )
526+ call( true , false , true , GlobalTimer ) ,
527+ ( vec![ Task :: GlobalTimer ] , inc( GlobalTimer ) )
614528 ) ;
615529
616- test. canister_state_mut ( canister)
617- . system_state
618- . task_queue
619- . pop_front ( ) ;
620-
621- assert_eq ! ( heartbeat_and_timer_canisters, BTreeSet :: from( [ canister] ) ) ;
530+ // With all tree, the next method is always scheduled and advanced past.
531+ assert_eq ! ( call( true , true , true , Message ) , ( vec![ ] , inc( Message ) ) ) ;
532+ assert_eq ! (
533+ call( true , true , true , Heartbeat ) ,
534+ ( vec![ Task :: Heartbeat , Task :: GlobalTimer ] , inc( Heartbeat ) )
535+ ) ;
536+ assert_eq ! (
537+ call( true , true , true , GlobalTimer ) ,
538+ ( vec![ Task :: GlobalTimer , Task :: Heartbeat ] , inc( GlobalTimer ) )
539+ ) ;
622540}
623541
624542#[ test]
0 commit comments