337 lines
10 KiB
C++
337 lines
10 KiB
C++
#include "test_helper.h"
|
|
#include "compiler/rules/character_set.h"
|
|
|
|
using namespace rules;
|
|
|
|
START_TEST
|
|
|
|
describe("CharacterSet", []() {
|
|
describe("equality", [&]() {
|
|
it("returns true for identical character sets", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include('a', 'd')
|
|
.include('f', 'm');
|
|
|
|
CharacterSet set2 = CharacterSet()
|
|
.include('a', 'd')
|
|
.include('f', 'm');
|
|
|
|
AssertThat(set1, Equals(set2));
|
|
});
|
|
|
|
it("returns false for character sets that include different ranges", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include('a', 'd')
|
|
.include('f', 'm');
|
|
|
|
CharacterSet set2 = CharacterSet()
|
|
.include('a', 'c')
|
|
.include('f', 'm');
|
|
|
|
AssertThat(set1, !Equals(set2));
|
|
AssertThat(set2, !Equals(set1));
|
|
});
|
|
|
|
it("returns false for character sets that exclude different ranges", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'd')
|
|
.exclude('f', 'm');
|
|
|
|
CharacterSet set2 = CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'c')
|
|
.exclude('f', 'm');
|
|
|
|
AssertThat(set1, !Equals(set2));
|
|
AssertThat(set2, !Equals(set1));
|
|
});
|
|
|
|
it("returns false for character sets with different sign", [&]() {
|
|
CharacterSet set1 = CharacterSet().include_all();
|
|
CharacterSet set2 = CharacterSet();
|
|
|
|
AssertThat(set1, !Equals(set2));
|
|
AssertThat(set2, !Equals(set1));
|
|
});
|
|
});
|
|
|
|
describe("hashing", [&]() {
|
|
it("returns the same number for identical character sets", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include('a', 'd')
|
|
.include('f', 'm');
|
|
|
|
CharacterSet set2 = CharacterSet()
|
|
.include('a', 'd')
|
|
.include('f', 'm');
|
|
|
|
AssertThat(set1.hash_code(), Equals(set2.hash_code()));
|
|
});
|
|
|
|
it("returns different numbers for character sets that include different ranges", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include('a', 'd')
|
|
.include('f', 'm');
|
|
|
|
CharacterSet set2 = CharacterSet()
|
|
.include('a', 'c')
|
|
.include('f', 'm');
|
|
|
|
AssertThat(set1.hash_code(), !Equals(set2.hash_code()));
|
|
AssertThat(set2.hash_code(), !Equals(set1.hash_code()));
|
|
});
|
|
|
|
it("returns different numbers for character sets that exclude different ranges", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'd')
|
|
.exclude('f', 'm');
|
|
|
|
CharacterSet set2 = CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'c')
|
|
.exclude('f', 'm');
|
|
|
|
AssertThat(set1.hash_code(), !Equals(set2.hash_code()));
|
|
AssertThat(set2.hash_code(), !Equals(set1.hash_code()));
|
|
});
|
|
|
|
it("returns different numbers for character sets with different sign", [&]() {
|
|
CharacterSet set1 = CharacterSet().include_all();
|
|
CharacterSet set2 = CharacterSet();
|
|
|
|
AssertThat(set1.hash_code(), !Equals(set2.hash_code()));
|
|
AssertThat(set2.hash_code(), !Equals(set1.hash_code()));
|
|
});
|
|
});
|
|
|
|
describe("::is_empty", [&]() {
|
|
it("returns true for empty character sets", [&]() {
|
|
AssertThat(CharacterSet().is_empty(), Equals(true));
|
|
});
|
|
|
|
it("returns false for full character sets", [&]() {
|
|
AssertThat(CharacterSet().include_all().is_empty(), Equals(false));
|
|
});
|
|
|
|
it("returns false for character sets that include some characters", [&]() {
|
|
AssertThat(CharacterSet().include('x').is_empty(), Equals(false));
|
|
});
|
|
});
|
|
|
|
describe("::include", [&]() {
|
|
describe("when the set has a whitelist of characters", [&]() {
|
|
it("adds included characters", [&]() {
|
|
CharacterSet set1 = CharacterSet().include('a', 'd');
|
|
AssertThat(set1, Equals(CharacterSet()
|
|
.include('a')
|
|
.include('b')
|
|
.include('c')
|
|
.include('d')));
|
|
});
|
|
});
|
|
|
|
describe("when the set has a blacklist of characters", [&]() {
|
|
it("removes excluded characters", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'g')
|
|
.include('c', 'e');
|
|
|
|
AssertThat(set1, Equals(CharacterSet()
|
|
.include_all()
|
|
.exclude('a')
|
|
.exclude('b')
|
|
.exclude('f')
|
|
.exclude('g')));
|
|
});
|
|
|
|
it("does nothing if the character are already not excluded", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include_all()
|
|
.include('a', 'c');
|
|
|
|
AssertThat(set1, Equals(CharacterSet().include_all()));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("::exclude", [&]() {
|
|
describe("when the set has a whitelist of characters", [&]() {
|
|
it("removes included characters", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include('a', 'g')
|
|
.exclude('c', 'e');
|
|
|
|
AssertThat(set1, Equals(CharacterSet()
|
|
.include('a')
|
|
.include('b')
|
|
.include('f')
|
|
.include('g')));
|
|
});
|
|
|
|
it("does nothing if the character's are already not included", [&]() {
|
|
CharacterSet set1 = CharacterSet().exclude('a', 'c');
|
|
AssertThat(set1, Equals(CharacterSet()));
|
|
});
|
|
});
|
|
|
|
describe("when the set has a blacklist of characters", [&]() {
|
|
it("removes excluded characters", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'd');
|
|
|
|
AssertThat(set1, Equals(CharacterSet()
|
|
.include_all()
|
|
.exclude('a')
|
|
.exclude('b')
|
|
.exclude('c')
|
|
.exclude('d')));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("::remove_set", []() {
|
|
CharacterSet intersection;
|
|
|
|
describe("for a set with whitelisted characters", [&]() {
|
|
describe("when the subtractend has whitelisted characters", [&]() {
|
|
it("removes the included characters that the other set also includes", [&]() {
|
|
CharacterSet set1 = CharacterSet().include('a', 'z');
|
|
set1.remove_set(CharacterSet().include('d', 's'));
|
|
AssertThat(set1, Equals(CharacterSet()
|
|
.include('a', 'c')
|
|
.include('t', 'z')));
|
|
});
|
|
|
|
it("returns the characters that were removed", [&]() {
|
|
CharacterSet set1 = CharacterSet().include('a', 'z');
|
|
intersection = set1.remove_set(CharacterSet().include('d', 's'));
|
|
AssertThat(intersection, Equals(CharacterSet()
|
|
.include('d', 's')));
|
|
});
|
|
|
|
it("returns the empty set when the sets are disjoint", [&]() {
|
|
CharacterSet set1 = CharacterSet().include('a', 'z');
|
|
intersection = set1.remove_set(CharacterSet().include('A', 'Z'));
|
|
AssertThat(set1, Equals(CharacterSet().include('a', 'z')));
|
|
AssertThat(intersection, Equals(CharacterSet()));
|
|
});
|
|
});
|
|
|
|
describe("when the subtractend has blacklisted characters", [&]() {
|
|
it("removes the included characters that are not excluded by the other set", [&]() {
|
|
CharacterSet set1 = CharacterSet().include('a', 'f');
|
|
|
|
intersection = set1.remove_set(CharacterSet()
|
|
.include_all()
|
|
.exclude('d', 'z'));
|
|
|
|
AssertThat(set1, Equals(CharacterSet()
|
|
.include('d', 'f')));
|
|
AssertThat(intersection, Equals(CharacterSet()
|
|
.include('a', 'c')));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("for a set with blacklisted characters", [&]() {
|
|
describe("when the subtractend has whitelisted characters", [&]() {
|
|
it("adds the subtractend's inclusions to the receiver's exclusions", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'f');
|
|
|
|
intersection = set1.remove_set(CharacterSet()
|
|
.include('x', 'z'));
|
|
|
|
AssertThat(set1, Equals(CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'f')
|
|
.exclude('x', 'z')));
|
|
|
|
AssertThat(intersection, Equals(CharacterSet().include('x', 'z')));
|
|
});
|
|
});
|
|
|
|
describe("when the subtractend has blacklisted characters", [&]() {
|
|
it("includes only the characters excluded by the subtractend but not by the receiver", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'm');
|
|
|
|
set1.remove_set(CharacterSet()
|
|
.include_all()
|
|
.exclude('d', 'z'));
|
|
|
|
AssertThat(set1, Equals(CharacterSet()
|
|
.include('n', 'z')));
|
|
});
|
|
|
|
it("returns the characters excluded by neither set", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'm');
|
|
|
|
intersection = set1.remove_set(CharacterSet()
|
|
.include_all()
|
|
.exclude('d', 'z'));
|
|
|
|
AssertThat(intersection, Equals(CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'z')));
|
|
});
|
|
|
|
it("works when the sets are disjoint", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'm');
|
|
|
|
intersection = set1.remove_set(CharacterSet()
|
|
.include_all()
|
|
.exclude('d', 'z'));
|
|
|
|
AssertThat(set1, Equals(CharacterSet()
|
|
.include('n', 'z')));
|
|
|
|
AssertThat(intersection, Equals(CharacterSet()
|
|
.include_all()
|
|
.exclude('a', 'z')));
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("::included_ranges", [&]() {
|
|
it("consolidates sequences of 3 or more consecutive characters into ranges", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include('a', 'c')
|
|
.include('g')
|
|
.include('z');
|
|
|
|
AssertThat(set1.included_ranges(), Equals(vector<CharacterRange>({
|
|
CharacterRange('a', 'c'),
|
|
CharacterRange('g'),
|
|
CharacterRange('z'),
|
|
})));
|
|
});
|
|
|
|
it("doesn't consolidate sequences of 2 consecutive characters", [&]() {
|
|
CharacterSet set1 = CharacterSet()
|
|
.include('a', 'b')
|
|
.include('g')
|
|
.include('z');
|
|
|
|
AssertThat(set1.included_ranges(), Equals(vector<CharacterRange>({
|
|
CharacterRange('a'),
|
|
CharacterRange('b'),
|
|
CharacterRange('g'),
|
|
CharacterRange('z'),
|
|
})));
|
|
});
|
|
});
|
|
});
|
|
|
|
END_TEST
|