From 565823c904aa2529d9f74a20ae63075adad26b72 Mon Sep 17 00:00:00 2001 From: Vladislav Polyakov Date: Wed, 21 Apr 2021 13:20:17 +0300 Subject: [PATCH 1/9] feat: add ready for use instance with caching --- lib/rrule.dart | 1 + lib/src/cache.dart | 39 +++++++++++++++ lib/src/rrule.dart | 79 ++++++++++++++++++++++++++++++ test/cache_test.dart | 52 ++++++++++++++++++++ test/rrule_test.dart | 112 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 283 insertions(+) create mode 100644 lib/src/cache.dart create mode 100644 lib/src/rrule.dart create mode 100644 test/cache_test.dart create mode 100644 test/rrule_test.dart diff --git a/lib/rrule.dart b/lib/rrule.dart index 95a3271..51e94d3 100644 --- a/lib/rrule.dart +++ b/lib/rrule.dart @@ -7,3 +7,4 @@ export 'src/codecs/string/string.dart' show RecurrenceRuleStringCodec; export 'src/codecs/text/l10n/l10n.dart'; export 'src/frequency.dart' show Frequency; export 'src/recurrence_rule.dart' show RecurrenceRule; +export 'src/rrule.dart' show RRule; \ No newline at end of file diff --git a/lib/src/cache.dart b/lib/src/cache.dart new file mode 100644 index 0000000..306f661 --- /dev/null +++ b/lib/src/cache.dart @@ -0,0 +1,39 @@ +import 'package:meta/meta.dart'; + +enum CacheMethod { before, after, between } + +class Cache { + final Map> _before = {}; + @visibleForTesting + Map> get beforeResults => _before; + + final Map> _after = {}; + @visibleForTesting + Map> get afterResults => _after; + + final Map> _between = {}; + @visibleForTesting + Map> get betweenResults => _between; + + void add(CacheMethod method, int argumentsHash, List data) { + switch (method) { + case CacheMethod.before: + return _before.addAll({argumentsHash: data}); + case CacheMethod.after: + return _after.addAll({argumentsHash: data}); + case CacheMethod.between: + return _between.addAll({argumentsHash: data}); + } + } + + List? get(CacheMethod method, int argumentsHash) { + switch (method) { + case CacheMethod.before: + return _before[argumentsHash]; + case CacheMethod.after: + return _after[argumentsHash]; + case CacheMethod.between: + return _between[argumentsHash]; + } + } +} diff --git a/lib/src/rrule.dart b/lib/src/rrule.dart new file mode 100644 index 0000000..214d8e2 --- /dev/null +++ b/lib/src/rrule.dart @@ -0,0 +1,79 @@ +import 'package:meta/meta.dart'; + +import 'cache.dart'; +import 'recurrence_rule.dart'; +import 'utils.dart'; + +class RRule { + RRule({required this.rule, required this.start, Cache? initialCache}) { + if (initialCache != null) _cache = initialCache; + } + + final RecurrenceRule rule; + + final DateTime start; + + Cache _cache = Cache(); + + @visibleForTesting + Cache get cache => _cache; + + List before({ + required DateTime date, + bool inc = false, + }) { + final instances = rule.getInstances(start: start); + final argumentsHash = hashList([instances, date, inc]); + final fromCache = _cache.get(CacheMethod.before, argumentsHash); + if (fromCache != null) return fromCache; + + final result = instances.takeWhile(inc ? _le(date) : _l(date)).toList(); + + _cache.add(CacheMethod.before, argumentsHash, result); + + return result; + } + + List after({required DateTime date, bool inc = false}) { + assert(rule.until != null); + + final instances = rule.getInstances(start: start); + final argumentsHash = hashList([instances, date, inc]); + final fromCache = _cache.get(CacheMethod.after, argumentsHash); + if (fromCache != null) return fromCache; + + final result = instances.skipWhile(inc ? _l(date) : _le(date)).toList(); + + _cache.add(CacheMethod.after, argumentsHash, result); + + return result; + } + + List between({ + required DateTime start, + required DateTime end, + bool inc = false, + }) { + final instances = rule.getInstances(start: start); + final argumentsHash = hashList([instances, start, end, inc]); + final fromCache = _cache.get(CacheMethod.between, argumentsHash); + if (fromCache != null) return fromCache; + + final result = instances + .skipWhile(inc ? _l(start) : _le(start)) + .takeWhile(inc ? _le(end) : _l(end)) + .toList(); + + _cache.add(CacheMethod.between, argumentsHash, result); + + return result; + } + + bool Function(DateTime date) _l(DateTime date) { + return (value) => value < date; + } + + bool Function(DateTime date) _le(DateTime date) { + return (value) => value <= date; + } +} diff --git a/test/cache_test.dart b/test/cache_test.dart new file mode 100644 index 0000000..808b9ac --- /dev/null +++ b/test/cache_test.dart @@ -0,0 +1,52 @@ +import 'package:rrule/src/cache.dart'; +import 'package:test/test.dart'; + +void main() { + test('should add data to the cache of the before method', () { + final cache = Cache(); + final results = []; + cache.add(CacheMethod.before, 1, results); + + expect(cache.beforeResults, containsPair(1, results)); + }); + + test('should add data to the cache of the after method', () { + final cache = Cache(); + final results = []; + cache.add(CacheMethod.after, 1, results); + + expect(cache.afterResults, containsPair(1, results)); + }); + + test('should add data to the cache of the between method', () { + final cache = Cache(); + final results = []; + cache.add(CacheMethod.between, 1, results); + + expect(cache.betweenResults, containsPair(1, results)); + }); + + test('should return data from the cache of the before method', () { + final cache = Cache(); + final results = []; + cache.add(CacheMethod.before, 1, results); + + expect(cache.get(CacheMethod.before, 1), results); + }); + + test('should return data from the cache of the after method', () { + final cache = Cache(); + final results = []; + cache.add(CacheMethod.after, 1, results); + + expect(cache.get(CacheMethod.after, 1), results); + }); + + test('should return data from the cache of the between method', () { + final cache = Cache(); + final results = []; + cache.add(CacheMethod.between, 1, results); + + expect(cache.get(CacheMethod.between, 1), results); + }); +} diff --git a/test/rrule_test.dart b/test/rrule_test.dart new file mode 100644 index 0000000..bac78fb --- /dev/null +++ b/test/rrule_test.dart @@ -0,0 +1,112 @@ +import 'package:rrule/rrule.dart'; +import 'package:rrule/src/cache.dart'; +import 'package:rrule/src/rrule.dart'; +import 'package:test/test.dart'; + +void main() { + test('should be initialized with the initial cache', () { + final cache = Cache(); + cache.add(CacheMethod.before, 1, []); + + final rule = RecurrenceRule(frequency: Frequency.daily); + final start = DateTime.utc(2020); + final rrule = RRule(rule: rule, start: start, initialCache: cache); + + expect(rrule.cache.beforeResults, isNotEmpty); + }); + + test('should return valid list of dates for the method before', () { + final rule = RecurrenceRule(frequency: Frequency.monthly); + final start = DateTime.utc(2020); + final rrule = RRule(rule: rule, start: start); + + expect(rrule.before(date: DateTime.utc(2020, 3)), [ + DateTime.utc(2020, 1), + DateTime.utc(2020, 2), + ]); + + expect( + rrule.before(date: DateTime.utc(2020, 3), inc: true).last, + DateTime.utc(2020, 3), + ); + }); + + test('should return valid list of dates for the method after', () { + final start = DateTime.utc(2020); + final end = DateTime.utc(2020, 3); + final rule = RecurrenceRule(frequency: Frequency.monthly, until: end); + final rrule = RRule(rule: rule, start: start); + + expect(rrule.after(date: DateTime.utc(2020, 1)), [ + DateTime.utc(2020, 2), + DateTime.utc(2020, 3), + ]); + + expect( + rrule.after(date: DateTime.utc(2020, 1), inc: true).first, + DateTime.utc(2020, 1), + ); + }); + + test('should return valid list of dates for the method between', () { + final start = DateTime.utc(2020); + final rule = RecurrenceRule(frequency: Frequency.monthly); + final rrule = RRule(rule: rule, start: start); + + expect( + rrule.between(start: DateTime.utc(2020, 2), end: DateTime.utc(2020, 4)), + [DateTime.utc(2020, 3)], + ); + + expect( + rrule.between( + start: DateTime.utc(2020, 2), + end: DateTime.utc(2020, 4), + inc: true, + ), + [DateTime.utc(2020, 2), DateTime.utc(2020, 3), DateTime.utc(2020, 4)], + ); + }); + + group('should add data to the cache of the corresponding method', () { + test('before', () { + final rule = RecurrenceRule(frequency: Frequency.daily); + final start = DateTime.utc(2020); + final rrule = RRule(rule: rule, start: start); + + final beforeResult = rrule.before(date: DateTime(2020, 2)); + final cacheResults = rrule.cache.beforeResults; + expect(beforeResult, cacheResults.values.first); + }); + + test('after', () { + final start = DateTime.utc(2020); + final end = DateTime.utc(2020, 3); + final rule = RecurrenceRule(frequency: Frequency.daily, until: end); + final rrule = RRule(rule: rule, start: start); + + final afterResult = rrule.after(date: DateTime(2020, 2)); + final cacheResults = rrule.cache.afterResults; + expect(afterResult, cacheResults.values.first); + }); + + test('between', () { + final start = DateTime.utc(2020); + final end = DateTime.utc(2020, 3); + final rule = RecurrenceRule(frequency: Frequency.daily, until: end); + final rrule = RRule(rule: rule, start: start); + + final betweenResult = rrule.between( + start: DateTime.utc(2020, 1), + end: DateTime.utc(2020, 2), + ); + + final cacheResults = rrule.cache.betweenResults; + expect(betweenResult, cacheResults.values.first); + }); + }); + + group('should return data from the cache of the corresponding method', () { + //TODO: add mockito for testing this logic + }); +} From 399fa073c6c0ecd9e1bf002f9c153019a1742383 Mon Sep 17 00:00:00 2001 From: Vladislav Polyakov Date: Mon, 26 Apr 2021 17:02:45 +0300 Subject: [PATCH 2/9] feat: create new method for getting all instances with cache support --- lib/rrule.dart | 1 - lib/src/cache.dart | 78 +++++++++++++-------- lib/src/iteration/iteration.dart | 17 +++++ lib/src/recurrence_rule.dart | 71 +++++++++++++++++++- lib/src/rrule.dart | 79 ---------------------- test/cache_test.dart | 46 +++---------- test/recurrence_rule_test.dart | 13 ++++ test/rrule_test.dart | 112 ------------------------------- 8 files changed, 157 insertions(+), 260 deletions(-) delete mode 100644 lib/src/rrule.dart delete mode 100644 test/rrule_test.dart diff --git a/lib/rrule.dart b/lib/rrule.dart index 51e94d3..95a3271 100644 --- a/lib/rrule.dart +++ b/lib/rrule.dart @@ -7,4 +7,3 @@ export 'src/codecs/string/string.dart' show RecurrenceRuleStringCodec; export 'src/codecs/text/l10n/l10n.dart'; export 'src/frequency.dart' show Frequency; export 'src/recurrence_rule.dart' show RecurrenceRule; -export 'src/rrule.dart' show RRule; \ No newline at end of file diff --git a/lib/src/cache.dart b/lib/src/cache.dart index 306f661..2b26142 100644 --- a/lib/src/cache.dart +++ b/lib/src/cache.dart @@ -1,39 +1,61 @@ import 'package:meta/meta.dart'; -enum CacheMethod { before, after, between } +import 'utils.dart'; + +class CacheKey { + CacheKey({ + required this.start, + this.after, + this.includeAfter = false, + this.before, + this.includeBefore = false, + }); + + DateTime start; + + DateTime? after; + + bool includeAfter; + + DateTime? before; + + bool includeBefore; + + @override + int get hashCode { + return hashList([ + start, + after, + includeAfter, + before, + includeBefore, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other.runtimeType != runtimeType) return false; + + return other is CacheKey && + other.after == after && + other.includeAfter == includeAfter && + other.before == before && + other.includeBefore == includeBefore; + } +} class Cache { - final Map> _before = {}; - @visibleForTesting - Map> get beforeResults => _before; + final Map> _results = {}; - final Map> _after = {}; @visibleForTesting - Map> get afterResults => _after; + Map> get results => _results; - final Map> _between = {}; - @visibleForTesting - Map> get betweenResults => _between; - - void add(CacheMethod method, int argumentsHash, List data) { - switch (method) { - case CacheMethod.before: - return _before.addAll({argumentsHash: data}); - case CacheMethod.after: - return _after.addAll({argumentsHash: data}); - case CacheMethod.between: - return _between.addAll({argumentsHash: data}); - } + void add(CacheKey key, List data) { + _results.addAll({key: data}); } - List? get(CacheMethod method, int argumentsHash) { - switch (method) { - case CacheMethod.before: - return _before[argumentsHash]; - case CacheMethod.after: - return _after[argumentsHash]; - case CacheMethod.between: - return _between[argumentsHash]; - } + List? get(CacheKey key) { + return _results[key]; } } diff --git a/lib/src/iteration/iteration.dart b/lib/src/iteration/iteration.dart index f07fa05..47ca737 100644 --- a/lib/src/iteration/iteration.dart +++ b/lib/src/iteration/iteration.dart @@ -17,8 +17,16 @@ import 'time_set.dart'; Iterable getRecurrenceRuleInstances( RecurrenceRule rrule, { required DateTime start, + DateTime? after, + bool includeAfter = false, + DateTime? before, + bool includeBefore = false, }) sync* { assert(start.isValidRruleDateTime); + assert(after.isValidRruleDateTime); + assert(before.isValidRruleDateTime); + if (after != null) assert(after >= start); + if (before != null) assert(before >= start); rrule = _prepare(rrule, start); @@ -44,7 +52,16 @@ Iterable getRecurrenceRuleInstances( for (final result in results) { if (rrule.until != null && result > rrule.until!) return; + if (before != null) { + if (!includeBefore && result >= before) return; + if (includeBefore && result > before) return; + } + if (result < start) continue; + if (after != null) { + if (!includeAfter && result <= after) continue; + if (includeAfter && result < after) continue; + } yield result; if (count != null) { diff --git a/lib/src/recurrence_rule.dart b/lib/src/recurrence_rule.dart index 135e174..43a2591 100644 --- a/lib/src/recurrence_rule.dart +++ b/lib/src/recurrence_rule.dart @@ -4,6 +4,7 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'by_week_day_entry.dart'; +import 'cache.dart'; import 'codecs/string/decoder.dart'; import 'codecs/string/string.dart'; import 'codecs/text/encoder.dart'; @@ -30,6 +31,7 @@ class RecurrenceRule { Set byMonths = const {}, Set bySetPositions = const {}, this.weekStart, + this.shouldCacheResults = false, }) : assert(count == null || count >= 1), assert(until.isValidRruleDateTime), assert(until == null || count == null), @@ -121,38 +123,47 @@ class RecurrenceRule { /// Corresponds to the `BYSECOND` property. final Set bySeconds; + bool get hasBySeconds => bySeconds.isNotEmpty; /// Corresponds to the `BYMINUTE` property. final Set byMinutes; + bool get hasByMinutes => byMinutes.isNotEmpty; /// Corresponds to the `BYHOUR` property. final Set byHours; + bool get hasByHours => byHours.isNotEmpty; /// Corresponds to the `BYDAY` property. final Set byWeekDays; + bool get hasByWeekDays => byWeekDays.isNotEmpty; /// Corresponds to the `BYMONTHDAY` property. final Set byMonthDays; + bool get hasByMonthDays => byMonthDays.isNotEmpty; /// Corresponds to the `BYYEARDAY` property. final Set byYearDays; + bool get hasByYearDays => byYearDays.isNotEmpty; /// Corresponds to the `BYWEEKNO` property. final Set byWeeks; + bool get hasByWeeks => byWeeks.isNotEmpty; /// Corresponds to the `BYMONTH` property. final Set byMonths; + bool get hasByMonths => byMonths.isNotEmpty; /// Corresponds to the `BYSETPOS` property. final Set bySetPositions; + bool get hasBySetPositions => bySetPositions.isNotEmpty; /// Corresponds to the `WKST` property. @@ -166,9 +177,65 @@ class RecurrenceRule { /// Returns [weekStart] or [DateTime.monday] if that is not set. int get actualWeekStart => weekStart ?? DateTime.monday; - Iterable getInstances({required DateTime start}) { + final bool shouldCacheResults; + + final Cache _cache = Cache(); + + @visibleForTesting + Cache get cache => _cache; + + Iterable getInstances({ + required DateTime start, + DateTime? after, + bool includeAfter = false, + DateTime? before, + bool includeBefore = false, + }) { assert(start.isValidRruleDateTime); - return getRecurrenceRuleInstances(this, start: start); + assert(after.isValidRruleDateTime); + assert(before.isValidRruleDateTime); + + return getRecurrenceRuleInstances( + this, + start: start, + after: after, + includeAfter: includeAfter, + before: before, + includeBefore: includeBefore, + ); + } + + List getAllInstances({ + required DateTime start, + DateTime? after, + bool includeAfter = false, + DateTime? before, + bool includeBefore = false, + }) { + final key = CacheKey( + start: start, + after: after, + includeAfter: includeAfter, + before: before, + includeBefore: includeBefore, + ); + + final fromCache = _cache.get(key); + if (fromCache != null) return fromCache; + + final results = getInstances( + start: start, + after: after, + includeAfter: includeAfter, + before: before, + includeBefore: includeBefore, + ).toList(growable: false); + + if (shouldCacheResults) { + _cache.add(key, results); + } + + return results; } @override diff --git a/lib/src/rrule.dart b/lib/src/rrule.dart deleted file mode 100644 index 214d8e2..0000000 --- a/lib/src/rrule.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:meta/meta.dart'; - -import 'cache.dart'; -import 'recurrence_rule.dart'; -import 'utils.dart'; - -class RRule { - RRule({required this.rule, required this.start, Cache? initialCache}) { - if (initialCache != null) _cache = initialCache; - } - - final RecurrenceRule rule; - - final DateTime start; - - Cache _cache = Cache(); - - @visibleForTesting - Cache get cache => _cache; - - List before({ - required DateTime date, - bool inc = false, - }) { - final instances = rule.getInstances(start: start); - final argumentsHash = hashList([instances, date, inc]); - final fromCache = _cache.get(CacheMethod.before, argumentsHash); - if (fromCache != null) return fromCache; - - final result = instances.takeWhile(inc ? _le(date) : _l(date)).toList(); - - _cache.add(CacheMethod.before, argumentsHash, result); - - return result; - } - - List after({required DateTime date, bool inc = false}) { - assert(rule.until != null); - - final instances = rule.getInstances(start: start); - final argumentsHash = hashList([instances, date, inc]); - final fromCache = _cache.get(CacheMethod.after, argumentsHash); - if (fromCache != null) return fromCache; - - final result = instances.skipWhile(inc ? _l(date) : _le(date)).toList(); - - _cache.add(CacheMethod.after, argumentsHash, result); - - return result; - } - - List between({ - required DateTime start, - required DateTime end, - bool inc = false, - }) { - final instances = rule.getInstances(start: start); - final argumentsHash = hashList([instances, start, end, inc]); - final fromCache = _cache.get(CacheMethod.between, argumentsHash); - if (fromCache != null) return fromCache; - - final result = instances - .skipWhile(inc ? _l(start) : _le(start)) - .takeWhile(inc ? _le(end) : _l(end)) - .toList(); - - _cache.add(CacheMethod.between, argumentsHash, result); - - return result; - } - - bool Function(DateTime date) _l(DateTime date) { - return (value) => value < date; - } - - bool Function(DateTime date) _le(DateTime date) { - return (value) => value <= date; - } -} diff --git a/test/cache_test.dart b/test/cache_test.dart index 808b9ac..677a186 100644 --- a/test/cache_test.dart +++ b/test/cache_test.dart @@ -2,51 +2,21 @@ import 'package:rrule/src/cache.dart'; import 'package:test/test.dart'; void main() { - test('should add data to the cache of the before method', () { + test('should add data to the cache', () { final cache = Cache(); + final key = CacheKey(start: DateTime.now()); final results = []; - cache.add(CacheMethod.before, 1, results); + cache.add(key, results); - expect(cache.beforeResults, containsPair(1, results)); + expect(cache.results, containsPair(key, results)); }); - test('should add data to the cache of the after method', () { + test('should return date from the cache by key', () { final cache = Cache(); + final key = CacheKey(start: DateTime.now()); final results = []; - cache.add(CacheMethod.after, 1, results); + cache.add(key, results); - expect(cache.afterResults, containsPair(1, results)); - }); - - test('should add data to the cache of the between method', () { - final cache = Cache(); - final results = []; - cache.add(CacheMethod.between, 1, results); - - expect(cache.betweenResults, containsPair(1, results)); - }); - - test('should return data from the cache of the before method', () { - final cache = Cache(); - final results = []; - cache.add(CacheMethod.before, 1, results); - - expect(cache.get(CacheMethod.before, 1), results); - }); - - test('should return data from the cache of the after method', () { - final cache = Cache(); - final results = []; - cache.add(CacheMethod.after, 1, results); - - expect(cache.get(CacheMethod.after, 1), results); - }); - - test('should return data from the cache of the between method', () { - final cache = Cache(); - final results = []; - cache.add(CacheMethod.between, 1, results); - - expect(cache.get(CacheMethod.between, 1), results); + expect(cache.get(key), results); }); } diff --git a/test/recurrence_rule_test.dart b/test/recurrence_rule_test.dart index c08da34..99661c9 100644 --- a/test/recurrence_rule_test.dart +++ b/test/recurrence_rule_test.dart @@ -1,4 +1,5 @@ import 'package:rrule/rrule.dart'; +import 'package:rrule/src/cache.dart'; import 'package:test/test.dart'; void main() { @@ -49,6 +50,18 @@ void main() { }); }); + group('cache', () { + test('should store instances in the cache', () { + final rrule = RecurrenceRule( + frequency: Frequency.monthly, + until: DateTime.utc(2021), + ); + final instances = rrule.getAllInstances(start: DateTime.utc(2020)); + + expect(rrule.cache.get(CacheKey(start: DateTime.utc(2020))), instances); + }); + }); + test( '#19: No warning for creating a RRULE with BYWEEKNO, but with non-YEARLY frequency', () { diff --git a/test/rrule_test.dart b/test/rrule_test.dart deleted file mode 100644 index bac78fb..0000000 --- a/test/rrule_test.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:rrule/rrule.dart'; -import 'package:rrule/src/cache.dart'; -import 'package:rrule/src/rrule.dart'; -import 'package:test/test.dart'; - -void main() { - test('should be initialized with the initial cache', () { - final cache = Cache(); - cache.add(CacheMethod.before, 1, []); - - final rule = RecurrenceRule(frequency: Frequency.daily); - final start = DateTime.utc(2020); - final rrule = RRule(rule: rule, start: start, initialCache: cache); - - expect(rrule.cache.beforeResults, isNotEmpty); - }); - - test('should return valid list of dates for the method before', () { - final rule = RecurrenceRule(frequency: Frequency.monthly); - final start = DateTime.utc(2020); - final rrule = RRule(rule: rule, start: start); - - expect(rrule.before(date: DateTime.utc(2020, 3)), [ - DateTime.utc(2020, 1), - DateTime.utc(2020, 2), - ]); - - expect( - rrule.before(date: DateTime.utc(2020, 3), inc: true).last, - DateTime.utc(2020, 3), - ); - }); - - test('should return valid list of dates for the method after', () { - final start = DateTime.utc(2020); - final end = DateTime.utc(2020, 3); - final rule = RecurrenceRule(frequency: Frequency.monthly, until: end); - final rrule = RRule(rule: rule, start: start); - - expect(rrule.after(date: DateTime.utc(2020, 1)), [ - DateTime.utc(2020, 2), - DateTime.utc(2020, 3), - ]); - - expect( - rrule.after(date: DateTime.utc(2020, 1), inc: true).first, - DateTime.utc(2020, 1), - ); - }); - - test('should return valid list of dates for the method between', () { - final start = DateTime.utc(2020); - final rule = RecurrenceRule(frequency: Frequency.monthly); - final rrule = RRule(rule: rule, start: start); - - expect( - rrule.between(start: DateTime.utc(2020, 2), end: DateTime.utc(2020, 4)), - [DateTime.utc(2020, 3)], - ); - - expect( - rrule.between( - start: DateTime.utc(2020, 2), - end: DateTime.utc(2020, 4), - inc: true, - ), - [DateTime.utc(2020, 2), DateTime.utc(2020, 3), DateTime.utc(2020, 4)], - ); - }); - - group('should add data to the cache of the corresponding method', () { - test('before', () { - final rule = RecurrenceRule(frequency: Frequency.daily); - final start = DateTime.utc(2020); - final rrule = RRule(rule: rule, start: start); - - final beforeResult = rrule.before(date: DateTime(2020, 2)); - final cacheResults = rrule.cache.beforeResults; - expect(beforeResult, cacheResults.values.first); - }); - - test('after', () { - final start = DateTime.utc(2020); - final end = DateTime.utc(2020, 3); - final rule = RecurrenceRule(frequency: Frequency.daily, until: end); - final rrule = RRule(rule: rule, start: start); - - final afterResult = rrule.after(date: DateTime(2020, 2)); - final cacheResults = rrule.cache.afterResults; - expect(afterResult, cacheResults.values.first); - }); - - test('between', () { - final start = DateTime.utc(2020); - final end = DateTime.utc(2020, 3); - final rule = RecurrenceRule(frequency: Frequency.daily, until: end); - final rrule = RRule(rule: rule, start: start); - - final betweenResult = rrule.between( - start: DateTime.utc(2020, 1), - end: DateTime.utc(2020, 2), - ); - - final cacheResults = rrule.cache.betweenResults; - expect(betweenResult, cacheResults.values.first); - }); - }); - - group('should return data from the cache of the corresponding method', () { - //TODO: add mockito for testing this logic - }); -} From 7ff8558e444366af5fd7628f0cce573402a8e435 Mon Sep 17 00:00:00 2001 From: Vladislav Polyakov Date: Mon, 26 Apr 2021 17:13:49 +0300 Subject: [PATCH 3/9] test: add tests for date after parameter --- lib/src/recurrence_rule.dart | 1 + test/recurrence_rule_test.dart | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/src/recurrence_rule.dart b/lib/src/recurrence_rule.dart index 43a2591..d5bb4a1 100644 --- a/lib/src/recurrence_rule.dart +++ b/lib/src/recurrence_rule.dart @@ -1,4 +1,5 @@ import 'dart:collection'; +import 'dart:math'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; diff --git a/test/recurrence_rule_test.dart b/test/recurrence_rule_test.dart index 99661c9..7d0465d 100644 --- a/test/recurrence_rule_test.dart +++ b/test/recurrence_rule_test.dart @@ -55,6 +55,7 @@ void main() { final rrule = RecurrenceRule( frequency: Frequency.monthly, until: DateTime.utc(2021), + shouldCacheResults: true, ); final instances = rrule.getAllInstances(start: DateTime.utc(2020)); @@ -62,6 +63,37 @@ void main() { }); }); + group('getAllInstances', () { + test('should support date after inclusive', () { + final rrule = RecurrenceRule( + frequency: Frequency.monthly, + until: DateTime.utc(2021), + ); + + final instances = rrule.getAllInstances( + start: DateTime.utc(2020), + after: DateTime.utc(2020, 5), + includeAfter: true, + ); + + expect(instances.first, DateTime.utc(2020, 5)); + }); + + test('should support date after exclusive', () { + final rrule = RecurrenceRule( + frequency: Frequency.monthly, + until: DateTime.utc(2021), + ); + + final instances = rrule.getAllInstances( + start: DateTime.utc(2020), + after: DateTime.utc(2020, 5), + ); + + expect(instances.first, DateTime.utc(2020, 6)); + }); + }); + test( '#19: No warning for creating a RRULE with BYWEEKNO, but with non-YEARLY frequency', () { From 8cfae297db32f1cae5c6463a3cf15dc6112d40d1 Mon Sep 17 00:00:00 2001 From: Vladislav Polyakov Date: Mon, 26 Apr 2021 17:15:29 +0300 Subject: [PATCH 4/9] test: add tests for date before parameter --- test/recurrence_rule_test.dart | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/recurrence_rule_test.dart b/test/recurrence_rule_test.dart index 7d0465d..4dc56e1 100644 --- a/test/recurrence_rule_test.dart +++ b/test/recurrence_rule_test.dart @@ -92,6 +92,35 @@ void main() { expect(instances.first, DateTime.utc(2020, 6)); }); + + test('should support date before inclusive', () { + final rrule = RecurrenceRule( + frequency: Frequency.monthly, + until: DateTime.utc(2021), + ); + + final instances = rrule.getAllInstances( + start: DateTime.utc(2020), + before: DateTime.utc(2020, 5), + includeBefore: true, + ); + + expect(instances.last, DateTime.utc(2020, 5)); + }); + + test('should support date before exclusive', () { + final rrule = RecurrenceRule( + frequency: Frequency.monthly, + until: DateTime.utc(2021), + ); + + final instances = rrule.getAllInstances( + start: DateTime.utc(2020), + before: DateTime.utc(2020, 5), + ); + + expect(instances.last, DateTime.utc(2020, 4)); + }); }); test( From 09702947294583b910b7012b67947b9d38a35a0d Mon Sep 17 00:00:00 2001 From: Vladislav Polyakov <39828645+polRk@users.noreply.github.com> Date: Mon, 3 May 2021 14:35:36 +0300 Subject: [PATCH 5/9] fix: remove empty spaces Co-authored-by: Jonas Wanke --- lib/src/cache.dart | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/src/cache.dart b/lib/src/cache.dart index 2b26142..b16997f 100644 --- a/lib/src/cache.dart +++ b/lib/src/cache.dart @@ -11,15 +11,11 @@ class CacheKey { this.includeBefore = false, }); - DateTime start; - - DateTime? after; - - bool includeAfter; - - DateTime? before; - - bool includeBefore; + final DateTime start; + final DateTime? after; + final bool includeAfter; + final DateTime? before; + final bool includeBefore; @override int get hashCode { From 9647f22cc91534508d65420057082e1233a09794 Mon Sep 17 00:00:00 2001 From: Vladislav Polyakov <39828645+polRk@users.noreply.github.com> Date: Mon, 3 May 2021 14:36:04 +0300 Subject: [PATCH 6/9] feat: make class immutable Co-authored-by: Jonas Wanke --- lib/src/cache.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/cache.dart b/lib/src/cache.dart index b16997f..ae6b71a 100644 --- a/lib/src/cache.dart +++ b/lib/src/cache.dart @@ -2,8 +2,9 @@ import 'package:meta/meta.dart'; import 'utils.dart'; +@immutable class CacheKey { - CacheKey({ + const CacheKey({ required this.start, this.after, this.includeAfter = false, From e5cc2e1deed6789068f47562e7bdc456872f4841 Mon Sep 17 00:00:00 2001 From: Vladislav Polyakov <39828645+polRk@users.noreply.github.com> Date: Mon, 3 May 2021 14:36:28 +0300 Subject: [PATCH 7/9] refactor: use array key assigment Co-authored-by: Jonas Wanke --- lib/src/cache.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/cache.dart b/lib/src/cache.dart index ae6b71a..1bd3d6e 100644 --- a/lib/src/cache.dart +++ b/lib/src/cache.dart @@ -49,7 +49,7 @@ class Cache { Map> get results => _results; void add(CacheKey key, List data) { - _results.addAll({key: data}); + _results[key]= data; } List? get(CacheKey key) { From d927ecfbbef0d514253634e76b594db97114a76d Mon Sep 17 00:00:00 2001 From: Vladislav Polyakov <39828645+polRk@users.noreply.github.com> Date: Mon, 3 May 2021 14:36:38 +0300 Subject: [PATCH 8/9] fix: typo Co-authored-by: Jonas Wanke --- test/cache_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cache_test.dart b/test/cache_test.dart index 677a186..b7e49f8 100644 --- a/test/cache_test.dart +++ b/test/cache_test.dart @@ -11,7 +11,7 @@ void main() { expect(cache.results, containsPair(key, results)); }); - test('should return date from the cache by key', () { + test('should return data from the cache by key', () { final cache = Cache(); final key = CacheKey(start: DateTime.now()); final results = []; From ee6803e8483be1b9e02ae6abbab0a54a3c5c3167 Mon Sep 17 00:00:00 2001 From: Vladislav Polyakov <39828645+polRk@users.noreply.github.com> Date: Mon, 3 May 2021 14:41:23 +0300 Subject: [PATCH 9/9] refactor: remote empty lines --- lib/src/recurrence_rule.dart | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/src/recurrence_rule.dart b/lib/src/recurrence_rule.dart index d5bb4a1..23ebb58 100644 --- a/lib/src/recurrence_rule.dart +++ b/lib/src/recurrence_rule.dart @@ -124,47 +124,38 @@ class RecurrenceRule { /// Corresponds to the `BYSECOND` property. final Set bySeconds; - bool get hasBySeconds => bySeconds.isNotEmpty; /// Corresponds to the `BYMINUTE` property. final Set byMinutes; - bool get hasByMinutes => byMinutes.isNotEmpty; /// Corresponds to the `BYHOUR` property. final Set byHours; - bool get hasByHours => byHours.isNotEmpty; /// Corresponds to the `BYDAY` property. final Set byWeekDays; - bool get hasByWeekDays => byWeekDays.isNotEmpty; /// Corresponds to the `BYMONTHDAY` property. final Set byMonthDays; - bool get hasByMonthDays => byMonthDays.isNotEmpty; /// Corresponds to the `BYYEARDAY` property. final Set byYearDays; - bool get hasByYearDays => byYearDays.isNotEmpty; /// Corresponds to the `BYWEEKNO` property. final Set byWeeks; - bool get hasByWeeks => byWeeks.isNotEmpty; /// Corresponds to the `BYMONTH` property. final Set byMonths; - bool get hasByMonths => byMonths.isNotEmpty; /// Corresponds to the `BYSETPOS` property. final Set bySetPositions; - bool get hasBySetPositions => bySetPositions.isNotEmpty; /// Corresponds to the `WKST` property.