@@ -172,6 +172,8 @@ func TestSubBandScheduleRestricted(t *testing.T) {
172172}
173173
174174func TestScheduleAnytimeRestricted (t * testing.T ) {
175+ t .Parallel ()
176+
175177 a := assertions .New (t )
176178 params := scheduling.SubBandParameters {
177179 MinFrequency : 0 ,
@@ -204,7 +206,8 @@ func TestScheduleAnytimeRestricted(t *testing.T) {
204206 from += scheduling .ConcentratorTime (time .Second )
205207 return res
206208 }
207- em , err := sb .ScheduleAnytime (time .Second , next , ttnpb .TxSchedulePriority_NORMAL )
209+ now := scheduling .ConcentratorTime (time .Now ().UnixNano ())
210+ em , err := sb .ScheduleAnytime (time .Second , next , ttnpb .TxSchedulePriority_NORMAL , now )
208211 a .So (err , should .BeNil )
209212 a .So (em .Starts (), should .Equal , 16 * time .Second )
210213 // [ 1 2 4 3 ]
@@ -217,7 +220,8 @@ func TestScheduleAnytimeRestricted(t *testing.T) {
217220 next := func () scheduling.ConcentratorTime {
218221 return scheduling .ConcentratorTime (19 * time .Second )
219222 }
220- em , err := sb .ScheduleAnytime (time .Second , next , ttnpb .TxSchedulePriority_NORMAL )
223+ now := scheduling .ConcentratorTime (time .Now ().UnixNano ())
224+ em , err := sb .ScheduleAnytime (time .Second , next , ttnpb .TxSchedulePriority_NORMAL , now )
221225 a .So (err , should .BeNil )
222226 a .So (em .Starts (), should .Equal , 26 * time .Second )
223227 // [ 1 2 4 3 5]
@@ -229,11 +233,60 @@ func TestScheduleAnytimeRestricted(t *testing.T) {
229233 next := func () scheduling.ConcentratorTime {
230234 return scheduling .ConcentratorTime (19 * time .Second )
231235 }
232- _ , err := sb .ScheduleAnytime (5 * time .Second , next , ttnpb .TxSchedulePriority_NORMAL )
236+ now := scheduling .ConcentratorTime (time .Now ().UnixNano ())
237+ _ , err := sb .ScheduleAnytime (5 * time .Second , next , ttnpb .TxSchedulePriority_NORMAL , now )
233238 a .So (err , should .HaveSameErrorDefinitionAs , scheduling .ErrDutyCycle )
234239 }
235240}
236241
242+ func TestScheduleAnytimeTooFarAhead (t * testing.T ) {
243+ t .Parallel ()
244+ // In the test environment DutyCycleWindow = 10s and MaxScheduleAhead = 20s.
245+ a := assertions .New (t )
246+ params := scheduling.SubBandParameters {
247+ MinFrequency : 0 ,
248+ MaxFrequency : math .MaxUint64 ,
249+ DutyCycle : 0.5 ,
250+ }
251+ clock := & mockClock {}
252+ ceilings := map [ttnpb.TxSchedulePriority ]float32 {
253+ ttnpb .TxSchedulePriority_NORMAL : 0.5 , // usable = 0.25
254+ ttnpb .TxSchedulePriority_HIGHEST : 1.0 , // usable = 0.50
255+ }
256+ sb := scheduling .NewSubBand (params , clock , ceilings , scheduling .DefaultDutyCycleStyle )
257+
258+ // Schedule a 3-second emission at t=9s using HIGHEST priority.
259+ // It occupies 30% of the duty-cycle window (10s), within HIGHEST (50%) but above
260+ // NORMAL (25%). Its window [9s, 12s] falls within the checkDutyCycle range
261+ // [0, 10s] for an emission starting at t=0, causing that check to fail.
262+ err := sb .Schedule (
263+ scheduling .NewEmission (scheduling .ConcentratorTime (9 * time .Second ), 3 * time .Second ),
264+ ttnpb .TxSchedulePriority_HIGHEST ,
265+ )
266+ a .So (err , should .BeNil )
267+
268+ // next() always returns the same ConcentratorTime, so ScheduleAnytime falls back
269+ // to the backwards scan. newT = 9s + 3s + 10s - 1s = 21s, which exceeds
270+ // now (0) + MaxScheduleAhead (20s).
271+ {
272+ next := func () scheduling.ConcentratorTime { return 0 }
273+ now := scheduling .ConcentratorTime (0 )
274+ _ , err := sb .ScheduleAnytime (time .Second , next , ttnpb .TxSchedulePriority_NORMAL , now )
275+ a .So (err , should .HaveSameErrorDefinitionAs , scheduling .ErrScheduleTooFarAhead )
276+ }
277+
278+ // With now shifted forward by 1 second, newT (21s) equals now + MaxScheduleAhead
279+ // (1s + 20s = 21s) exactly, so the check (strictly greater than) does not fire
280+ // and the emission is accepted.
281+ {
282+ next := func () scheduling.ConcentratorTime { return 0 }
283+ now := scheduling .ConcentratorTime (time .Second )
284+ em , err := sb .ScheduleAnytime (time .Second , next , ttnpb .TxSchedulePriority_NORMAL , now )
285+ a .So (err , should .BeNil )
286+ a .So (em .Starts (), should .Equal , 21 * time .Second )
287+ }
288+ }
289+
237290func TestBlockingScheduling (t * testing.T ) {
238291 t .Parallel ()
239292
0 commit comments