Skip to content

Commit ad78212

Browse files
authored
Reduce the chance of providing an unexpected duration for every() (#5)
+ All amounts are taken as the absolute value. + If amount is zero, throw an exception or we will end up with infinite streams of the same instant.
1 parent affc277 commit ad78212

File tree

8 files changed

+289
-13
lines changed

8 files changed

+289
-13
lines changed

src/main/java/com/ginsberg/timestream/LocalDateStream.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ public LocalDateStream every(int amount,
144144
this.amount = Math.abs(amount);
145145
this.unit = unit;
146146
LocalDate.now().plus(0, unit); // Fail fast test
147+
if (this.amount == 0) {
148+
throw new IllegalArgumentException("Amount must be non-zero");
149+
}
147150
return this;
148151
}
149152

@@ -156,11 +159,14 @@ public LocalDateStream every(int amount,
156159
* @throws java.time.temporal.UnsupportedTemporalTypeException if the period is not supported.
157160
* @see ChronoUnit
158161
*/
159-
public LocalDateStream every(Period period) {
162+
public LocalDateStream every(final Period period) {
160163
Objects.requireNonNull(period);
161164
this.unit = ChronoUnit.DAYS;
162-
this.amount = period.get(this.unit);
165+
this.amount = Math.abs(period.get(this.unit));
163166
LocalDate.now().plus(0, unit); // Fail fast test
167+
if (this.amount == 0) {
168+
throw new IllegalArgumentException("Effective amount must be non-zero (Period resolves to zero duration)");
169+
}
164170
return this;
165171
}
166172

src/main/java/com/ginsberg/timestream/LocalDateTimeStream.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ public LocalDateTimeStream every(int amount,
143143
Objects.requireNonNull(unit);
144144
this.amount = Math.abs(amount);
145145
this.unit = unit;
146+
if (this.amount == 0) {
147+
throw new IllegalArgumentException("Amount must be non-zero");
148+
}
146149
return this;
147150
}
148151

@@ -155,10 +158,13 @@ public LocalDateTimeStream every(int amount,
155158
* @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported.
156159
* @see ChronoUnit
157160
*/
158-
public LocalDateTimeStream every(Duration duration) {
161+
public LocalDateTimeStream every(final Duration duration) {
159162
Objects.requireNonNull(unit);
160163
this.unit = ChronoUnit.SECONDS;
161-
this.amount = duration.get(this.unit);
164+
this.amount = Math.abs(duration.get(this.unit));
165+
if (this.amount == 0) {
166+
throw new IllegalArgumentException("Effective amount must be non-zero (Duration resolves to zero duration)");
167+
}
162168
return this;
163169
}
164170

src/main/java/com/ginsberg/timestream/YearMonthStream.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ public YearMonthStream every(int amount,
144144
this.amount = Math.abs(amount);
145145
this.unit = unit;
146146
YearMonth.now().plus(0, unit); // Fail fast test
147+
if (this.amount == 0) {
148+
throw new IllegalArgumentException("Amount must be non-zero");
149+
}
147150
return this;
148151
}
149152

@@ -155,11 +158,14 @@ public YearMonthStream every(int amount,
155158
* @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported.
156159
* @see ChronoUnit
157160
*/
158-
public YearMonthStream every(Period period) {
161+
public YearMonthStream every(final Period period) {
159162
Objects.requireNonNull(unit);
160163
this.unit = ChronoUnit.MONTHS;
161-
this.amount = period.get(this.unit);
164+
this.amount = Math.abs(period.get(this.unit));
162165
YearMonth.now().plus(0, unit); // Fail fast test
166+
if (this.amount == 0) {
167+
throw new IllegalArgumentException("Effective amount must be non-zero (Period resolves to zero duration)");
168+
}
163169
return this;
164170
}
165171

src/main/java/com/ginsberg/timestream/ZonedDateTimeStream.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,22 +143,27 @@ public ZonedDateTimeStream every(int amount,
143143
Objects.requireNonNull(unit);
144144
this.amount = Math.abs(amount);
145145
this.unit = unit;
146+
if (this.amount == 0) {
147+
throw new IllegalArgumentException("Amount must be non-zero");
148+
}
146149
return this;
147150
}
148151

149152
/**
150153
* Set the duration between successive elements produced by the stream. The default
151154
* for this builder is 1 Second.
152155
*
153-
154156
* @return A non-null ZonedDateTimeStream.
155157
* @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported.
156158
* @see ChronoUnit
157159
*/
158-
public ZonedDateTimeStream every(Duration duration) {
160+
public ZonedDateTimeStream every(final Duration duration) {
159161
Objects.requireNonNull(unit);
160162
this.unit = ChronoUnit.SECONDS;
161-
this.amount = duration.get(this.unit);
163+
this.amount = Math.abs(duration.get(this.unit));
164+
if (this.amount == 0) {
165+
throw new IllegalArgumentException("Effective amount must be non-zero (Duration resolves to zero duration)");
166+
}
162167
return this;
163168
}
164169

src/test/java/com/ginsberg/timestream/LocalDateStreamTest.java

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,54 @@ public void stopsBeforeToWhenEveryIsAfterEndDate() {
100100
.containsExactly(now, now.plusDays(2));
101101
}
102102

103+
@Test
104+
public void negativeEveryUnitStillGoesForward() {
105+
final Stream<LocalDate> stream = LocalDateStream
106+
.from(now)
107+
.to(3, ChronoUnit.DAYS)
108+
.every(-2, ChronoUnit.DAYS)
109+
.stream();
110+
assertThat(stream)
111+
.isNotNull()
112+
.containsExactly(now, now.plusDays(2));
113+
}
114+
115+
@Test
116+
public void negativeEveryPeriodStillGoesForward() {
117+
final Stream<LocalDate> stream = LocalDateStream
118+
.from(now)
119+
.to(3, ChronoUnit.DAYS)
120+
.every(Period.parse("-P2D"))
121+
.stream();
122+
assertThat(stream)
123+
.isNotNull()
124+
.containsExactly(now, now.plusDays(2));
125+
}
126+
127+
@Test
128+
public void positiveEveryUnitStillGoesBackward() {
129+
final Stream<LocalDate> stream = LocalDateStream
130+
.from(now)
131+
.to(-3, ChronoUnit.DAYS)
132+
.every(2, ChronoUnit.DAYS)
133+
.stream();
134+
assertThat(stream)
135+
.isNotNull()
136+
.containsExactly(now, now.minusDays(2));
137+
}
138+
139+
@Test
140+
public void positiveEveryPeriodStillGoesBackward() {
141+
final Stream<LocalDate> stream = LocalDateStream
142+
.from(now)
143+
.to(-3, ChronoUnit.DAYS)
144+
.every(Period.parse("P2D"))
145+
.stream();
146+
assertThat(stream)
147+
.isNotNull()
148+
.containsExactly(now, now.minusDays(2));
149+
}
150+
103151
@Test
104152
public void identicalFromAndToCreateOnePointStream() {
105153
final Stream<LocalDate> stream = LocalDateStream
@@ -164,14 +212,29 @@ public void untilByUnitsMustHaveUnit() {
164212
LocalDateStream.fromNow().until(1, null);
165213
}
166214

215+
@Test(expected = NullPointerException.class)
216+
public void everyMustHavePeriod() {
217+
LocalDateStream.fromNow().every(null);
218+
}
219+
220+
@Test(expected = IllegalArgumentException.class)
221+
public void everyMustHaveNonZeroAmount() {
222+
LocalDateStream.fromNow().every(0, ChronoUnit.DAYS);
223+
}
224+
225+
@Test(expected = IllegalArgumentException.class)
226+
public void everyMustHaveNonZeroAmountFromPeriod() {
227+
LocalDateStream.fromNow().every(Period.parse("P0D"));
228+
}
229+
167230
@Test
168231
public void everyWithInvalidChronoUnitFailsFast() {
169-
expectingChronoUnitException(u -> LocalDateStream.fromNow().every(0, u), validChronoUnits);
232+
expectingChronoUnitException(u -> LocalDateStream.fromNow().every(1, u), validChronoUnits);
170233
}
171234

172235
@Test
173236
public void everyWithValidChronoUnit() {
174-
notExpectingChronoUnitException(u -> LocalDateStream.fromNow().every(0, u), validChronoUnits);
237+
notExpectingChronoUnitException(u -> LocalDateStream.fromNow().every(1, u), validChronoUnits);
175238
}
176239

177240
@Test

src/test/java/com/ginsberg/timestream/LocalDateTimeStreamTest.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,54 @@ public void stopsBeforeToWhenEveryDurationIsAfterEndDate() {
142142
.containsExactly(now, now.plusSeconds(2));
143143
}
144144

145+
@Test
146+
public void positiveEveryUnitStillGoesBackward() {
147+
final Stream<LocalDateTime> stream = LocalDateTimeStream
148+
.from(now)
149+
.to(-3, ChronoUnit.SECONDS)
150+
.every(2, ChronoUnit.SECONDS)
151+
.stream();
152+
assertThat(stream)
153+
.isNotNull()
154+
.containsExactly(now, now.minusSeconds(2));
155+
}
156+
157+
@Test
158+
public void positiveEveryDurationStillGoesBackward() {
159+
final Stream<LocalDateTime> stream = LocalDateTimeStream
160+
.from(now)
161+
.to(-3, ChronoUnit.SECONDS)
162+
.every(Duration.parse("PT2S"))
163+
.stream();
164+
assertThat(stream)
165+
.isNotNull()
166+
.containsExactly(now, now.minusSeconds(2));
167+
}
168+
169+
@Test
170+
public void negativeEveryUnitStillGoesForward() {
171+
final Stream<LocalDateTime> stream = LocalDateTimeStream
172+
.from(now)
173+
.to(3, ChronoUnit.SECONDS)
174+
.every(-2, ChronoUnit.SECONDS)
175+
.stream();
176+
assertThat(stream)
177+
.isNotNull()
178+
.containsExactly(now, now.plusSeconds(2));
179+
}
180+
181+
@Test
182+
public void negativeEveryDurationStillGoesForward() {
183+
final Stream<LocalDateTime> stream = LocalDateTimeStream
184+
.from(now)
185+
.to(3, ChronoUnit.SECONDS)
186+
.every(Duration.parse("-PT2S"))
187+
.stream();
188+
assertThat(stream)
189+
.isNotNull()
190+
.containsExactly(now, now.plusSeconds(2));
191+
}
192+
145193
@Test(expected = NullPointerException.class)
146194
public void mustHaveFromDate() {
147195
LocalDateTimeStream.from(null);
@@ -156,4 +204,19 @@ public void toByUnitsMustHaveUnit() {
156204
public void untilByUnitsMustHaveUnit() {
157205
LocalDateTimeStream.fromNow().until(1, null);
158206
}
207+
208+
@Test(expected = NullPointerException.class)
209+
public void everyMustHaveDuration() {
210+
LocalDateTimeStream.fromNow().every(null);
211+
}
212+
213+
@Test(expected = IllegalArgumentException.class)
214+
public void everyMustHaveNonZeroAmount() {
215+
LocalDateTimeStream.fromNow().every(0, ChronoUnit.SECONDS);
216+
}
217+
218+
@Test(expected = IllegalArgumentException.class)
219+
public void everyMustHaveNonZeroAmountFromPeriod() {
220+
LocalDateTimeStream.fromNow().every(Duration.parse("PT0S"));
221+
}
159222
}

src/test/java/com/ginsberg/timestream/YearMonthStreamTest.java

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,55 @@ public void stopsBeforeToWhenEveryIsAfterEndDate() {
9999
.containsExactly(now, now.plusMonths(2));
100100
}
101101

102+
@Test
103+
public void negativeEveryUnitStillGoesForward() {
104+
final Stream<YearMonth> stream = YearMonthStream
105+
.from(now)
106+
.to(3, ChronoUnit.MONTHS)
107+
.every(-2, ChronoUnit.MONTHS)
108+
.stream();
109+
assertThat(stream)
110+
.isNotNull()
111+
.containsExactly(now, now.plusMonths(2));
112+
}
113+
114+
115+
@Test
116+
public void negativeEveryPeriodStillGoesForward() {
117+
final Stream<YearMonth> stream = YearMonthStream
118+
.from(now)
119+
.to(3, ChronoUnit.MONTHS)
120+
.every(Period.parse("-P2M"))
121+
.stream();
122+
assertThat(stream)
123+
.isNotNull()
124+
.containsExactly(now, now.plusMonths(2));
125+
}
126+
127+
@Test
128+
public void positiveEveryUnitStillGoesBackward() {
129+
final Stream<YearMonth> stream = YearMonthStream
130+
.from(now)
131+
.to(-3, ChronoUnit.MONTHS)
132+
.every(2, ChronoUnit.MONTHS)
133+
.stream();
134+
assertThat(stream)
135+
.isNotNull()
136+
.containsExactly(now, now.minusMonths(2));
137+
}
138+
139+
@Test
140+
public void positiveEveryPeriodStillGoesBackward() {
141+
final Stream<YearMonth> stream = YearMonthStream
142+
.from(now)
143+
.to(-3, ChronoUnit.MONTHS)
144+
.every(Period.parse("P2M"))
145+
.stream();
146+
assertThat(stream)
147+
.isNotNull()
148+
.containsExactly(now, now.minusMonths(2));
149+
}
150+
102151
@Test
103152
public void identicalFromAndToCreateOnePointStream() {
104153
final Stream<YearMonth> stream = YearMonthStream
@@ -163,14 +212,29 @@ public void untilByUnitsMustHaveUnit() {
163212
YearMonthStream.fromNow().until(1, null);
164213
}
165214

215+
@Test(expected = NullPointerException.class)
216+
public void everyMustHavePeriod() {
217+
YearMonthStream.fromNow().every(null);
218+
}
219+
220+
@Test(expected = IllegalArgumentException.class)
221+
public void everyMustHaveNonZeroAmount() {
222+
YearMonthStream.fromNow().every(0, ChronoUnit.MONTHS);
223+
}
224+
225+
@Test(expected = IllegalArgumentException.class)
226+
public void everyMustHaveNonZeroAmountFromPeriod() {
227+
YearMonthStream.fromNow().every(Period.parse("P1W"));
228+
}
229+
166230
@Test
167231
public void everyWithInvalidChronoUnitFailsFast() {
168-
expectingChronoUnitException(u -> YearMonthStream.fromNow().every(0, u), validChronoUnits);
232+
expectingChronoUnitException(u -> YearMonthStream.fromNow().every(1, u), validChronoUnits);
169233
}
170234

171235
@Test
172236
public void everyWithValidChronoUnit() {
173-
notExpectingChronoUnitException(u -> YearMonthStream.fromNow().every(0, u), validChronoUnits);
237+
notExpectingChronoUnitException(u -> YearMonthStream.fromNow().every(1, u), validChronoUnits);
174238
}
175239

176240
@Test

0 commit comments

Comments
 (0)