Change:
<!-- Please only use this template for submitting reports about flaky tests or jobs (pass or fail with no underlying change in code) in Kubernetes CI --Which jobs are flaking: The unit test Which test(s) are flaking: Testgrid link: Reason for failure: In volume scheduling, we maintain internal caches that watch for PV/PVC events. However, we don't have a method to wait for them to be synced in UT. When the cache in the informer factory is synced but the execution of registered handlers may not finish. <!-- In production, this is not a problem. Even if the scheduler marks the pod uncacheable due to the stale cache in PVAssumeCache/PVCAssumeCache, the pod will be moved to the active queue later as the scheduler . related: --Anything else we need to know: links to appreciated links to specific failures in spyglass appreciated
/sig scheduling /sig storage
Even if I check the PV/PVC assume cache and wait for PV/PVC objects to be into the cache, the PV/PVC objects may not be . It seems that the shared informer with the fake client has a bug that registered event handlers may not be called (or handlers are not registered successfully).
The share informer with the fake client may miss events of objects which are created before caches are synced. it's better to create testing objects before starting the informers
Although the event handlers may not finish when the caches were synced in theory, however, it seems it's not necessary to wait for PV/PVC assume cache to be synced practically. I've run a thousand times (parallel: 10) with locally and no failures.

Discussion:
t.Fatal(err) } <del> // Start informer factory after initialization informerFactory.Start(ctx.Done()) // Feed testing data and wait for them to be synced </del> <ins> t.Log("Feed testing data and wait for them to be synced") </ins> client.StorageV1().StorageClasses().Create(ctx, immediateSC, metav1.CreateOptions{}) client.StorageV1().StorageClasses().Create(ctx, waitSC, metav1.CreateOptions{}) if item.node != nil {

for _, pv := range item.pvs { client.CoreV1().PersistentVolumes().Create(ctx, pv, metav1.CreateOptions{}) } <del> caches := informerFactory.WaitForCacheSync(ctx.Done()) for _, synced := range caches { if !synced { t.Errorf("error waiting for informer cache sync") } } // Verify </del> <ins> t.Log("Start informer factory after initialization") informerFactory.Start(ctx.Done()) t.Log("Wait for all started informers' cache were synced") informerFactory.WaitForCacheSync(ctx.Done()) t.Log("Verify") </ins> p := pl.(*VolumeBinding) nodeInfo := framework.NewNodeInfo() nodeInfo.SetNode(item.node) state := framework.NewCycleState() <del> t.Logf("call PreFilter and check status") </del> <ins> t.Logf("Verify: call PreFilter and check status") </ins> gotPreFilterStatus := p.PreFilter(ctx, state, item.pod) if !reflect.DeepEqual(gotPreFilterStatus, item.wantPreFilterStatus) { t.Errorf("filter prefilter status does not match: %v, want: %v", gotPreFilterStatus, item.wantPreFilterStatus)

// scheduler framework will skip Filter if PreFilter fails return } <del> t.Logf("check state after prefilter phase") </del> <ins> t.Logf("Verify: check state after prefilter phase") </ins> stateData, err := getStateData(state) if err != nil { t.Fatal(err)

if !reflect.DeepEqual(stateData, item.wantStateAfterPreFilter) { t.Errorf("state got after prefilter does not match: %v, want: %v", stateData, item.wantStateAfterPreFilter) } <del> t.Logf("call Filter and check status") </del> <ins> t.Logf("Verify: call Filter and check status") </ins> gotStatus := p.Filter(ctx, state, item.pod, nodeInfo) if !reflect.DeepEqual(gotStatus, item.wantFilterStatus) { t.Errorf("filter status does not match: %v, want: %v", gotStatus, item.wantFilterStatus)