diff options
Diffstat (limited to 'goldfishterm/internal/string_capability_test.cc')
-rw-r--r-- | goldfishterm/internal/string_capability_test.cc | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/goldfishterm/internal/string_capability_test.cc b/goldfishterm/internal/string_capability_test.cc new file mode 100644 index 0000000..a821551 --- /dev/null +++ b/goldfishterm/internal/string_capability_test.cc @@ -0,0 +1,372 @@ +// Copyright 2021 Benjamin Barenblat +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#include "goldfishterm/internal/string_capability.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <string.h> + +#include <ostream> +#include <stdexcept> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#include "goldfishterm/internal/emit.h" +#include "third_party/abseil/absl/strings/str_cat.h" +#include "third_party/abseil/absl/strings/string_view.h" +#include "third_party/abseil/absl/time/time.h" +#include "third_party/abseil/absl/types/optional.h" + +namespace goldfishterm_internal { +namespace { + +using ::testing::ElementsAre; +using ::testing::IsEmpty; +using ::testing::Pointee; +using ::testing::Property; +using ::testing::StrEq; +using ::testing::WhenDynamicCastTo; + +MATCHER_P(PointsToEmitBytes, s, + absl::StrCat("points to EmitBytes('", s, "')")) { + const char* start; + int length; + if constexpr (std::is_same_v<decltype(s), const char* const>) { + start = s; + length = strlen(s); + } else { + start = s.data(); + length = s.size(); + } + return ExplainMatchResult(Pointee(WhenDynamicCastTo<const EmitBytes&>( + EmitBytes(absl::string_view(start, length)))), + arg, result_listener); +} + +MATCHER_P(PointsToEmitDelay, t, + absl::StrCat("points to EmitDelay('", absl::FormatDuration(t), + "')")) { + return ExplainMatchResult( + Pointee(WhenDynamicCastTo<const EmitDelay&>(EmitDelay(t))), arg, + result_listener); +} + +InterpretStringCapabilityResult Interpret( + std::string text, std::vector<StringCapabilityParameter> parameters = {}) { + InterpretStringCapabilityInput input; + input.capability = std::move(text); + input.parameters = std::move(parameters); + return InterpretStringCapability(input); +} + +TEST(InterpretStringCapabilityTest, PadInteger) { + InterpretStringCapabilityInput input; + input.capability = "$<40>"; + input.baud = 8'000; // 1 character every millisecond + InterpretStringCapabilityResult result = InterpretStringCapability(input); + EXPECT_THAT(result.terms, + ElementsAre(PointsToEmitBytes(std::string(40, '\0')))); + EXPECT_EQ(result.cost, 40); +} + +TEST(InterpretStringCapabilityTest, PadFractional) { + InterpretStringCapabilityInput input; + input.capability = "$<.9>"; + input.baud = 80'000; // 10 characters every millisecond + InterpretStringCapabilityResult result = InterpretStringCapability(input); + EXPECT_THAT(result.terms, + ElementsAre(PointsToEmitBytes(std::string(9, '\0')))); + EXPECT_EQ(result.cost, 9); +} + +TEST(InterpretStringCapabilityTest, PadIgnoredIfMultipleSignificantFigures) { + EXPECT_THAT(Interpret("$<1.1>").terms, + ElementsAre(PointsToEmitBytes("$<1.1>"))); +} + +TEST(InterpretStringCapabilityTest, PerLinePad) { + InterpretStringCapabilityInput input; + input.capability = "$<4*>"; + input.lines_affected = 10; + input.baud = 8'000; + InterpretStringCapabilityResult result = InterpretStringCapability(input); + EXPECT_THAT(result.terms, + ElementsAre(PointsToEmitBytes(std::string(40, '\0')))); + EXPECT_EQ(result.cost, 40); +} + +TEST(InterpretStringCapabilityTest, PadIgnoredIfTerminalTooSlow) { + InterpretStringCapabilityInput input; + input.capability = "$<40>"; + input.padding_baud_rate = 115200; + input.baud = 9600; + InterpretStringCapabilityResult result = InterpretStringCapability(input); + EXPECT_THAT(result.terms, IsEmpty()); + EXPECT_EQ(result.cost, 0); +} + +TEST(InterpretStringCapabilityTest, PadWithWeirdCharacter) { + InterpretStringCapabilityInput input; + input.capability = "$<.1>"; + input.pad_char = 'a'; + input.baud = 9600; + InterpretStringCapabilityResult result = InterpretStringCapability(input); + EXPECT_THAT(result.terms, ElementsAre(PointsToEmitBytes("a"))); + EXPECT_EQ(result.cost, 1); +} + +TEST(InterpretStringCapabilityTest, SleepIfPadUnsupported) { + InterpretStringCapabilityInput input; + input.capability = "$<40>"; + input.pad_char = absl::nullopt; + input.baud = 16'000; + InterpretStringCapabilityResult result = InterpretStringCapability(input); + EXPECT_THAT(result.terms, + ElementsAre(PointsToEmitDelay(absl::Milliseconds(40)))); + EXPECT_EQ(result.cost, 80); // the equivalent of 80 characters +} + +TEST(InterpretStringCapabilityTest, PadWith8N1) { + InterpretStringCapabilityInput input; + input.capability = "$<40>"; + input.baud = 10'000; // 1 character every millisecond using + input.extra_bits_per_character = 2; // one start and one stop bit + InterpretStringCapabilityResult result = InterpretStringCapability(input); + EXPECT_THAT(result.terms, + ElementsAre(PointsToEmitBytes(std::string(40, '\0')))); + EXPECT_EQ(result.cost, 40); +} + +TEST(InterpretStringCapabilityTest, PadIgnoredIfXonXoffAvailable) { + InterpretStringCapabilityInput input; + input.capability = "$<40>"; + input.has_xon_xoff = true; + input.baud = 8'000; + InterpretStringCapabilityResult result = InterpretStringCapability(input); + EXPECT_THAT(result.terms, IsEmpty()); + EXPECT_EQ(result.cost, 40); +} + +TEST(InterpretStringCapabilityTest, MandatoryPadSentEvenIfXonXoffAvailable) { + InterpretStringCapabilityInput input; + input.capability = "$<40/>"; + input.has_xon_xoff = true; + input.baud = 8'000; + InterpretStringCapabilityResult result = InterpretStringCapability(input); + EXPECT_THAT(result.terms, + ElementsAre(PointsToEmitBytes(std::string(40, '\0')))); + EXPECT_EQ(result.cost, 40); +} + +TEST(InterpretStringCapabilityTest, Empty) { + InterpretStringCapabilityResult result = Interpret(""); + EXPECT_THAT(result.terms, IsEmpty()); + EXPECT_EQ(result.cost, 0); +} + +TEST(InterpretStringCapabilityTest, EmitLiteralPercent) { + InterpretStringCapabilityResult result = Interpret("%%"); + EXPECT_THAT(result.terms, ElementsAre(PointsToEmitBytes("%"))); + EXPECT_EQ(result.cost, 1); +} + +TEST(InterpretStringCapabilityTest, EmitChar) { + InterpretStringCapabilityResult result = Interpret("a"); + EXPECT_THAT(result.terms, ElementsAre(PointsToEmitBytes("a"))); + EXPECT_EQ(result.cost, 1); +} + +TEST(InterpretStringCapabilityTest, EmitText) { + InterpretStringCapabilityResult result = Interpret("hi"); + EXPECT_THAT(result.terms, ElementsAre(PointsToEmitBytes("hi"))); + EXPECT_EQ(result.cost, 2); +} + +TEST(InterpretStringCapabilityTest, EmitNonprinting) { + InterpretStringCapabilityResult result = Interpret("\r\n"); + EXPECT_THAT(result.terms, ElementsAre(PointsToEmitBytes("\r\n"))); + EXPECT_EQ(result.cost, 2); +} + +TEST(InterpretStringCapabilityTest, PushParameter) { + EXPECT_THAT(Interpret("%p2%d", {42, 96}).terms, + ElementsAre(PointsToEmitBytes("96"))); +} + +TEST(InterpretStringCapabilityTest, PopNumber) { + EXPECT_THAT(Interpret("%p1%:-10.5o", {42}).terms, + ElementsAre(PointsToEmitBytes("00052 "))); +} + +TEST(InterpretStringCapabilityTest, PopString) { + EXPECT_THAT(Interpret("%p1%:-10s", {"foo"}).terms, + ElementsAre(PointsToEmitBytes("foo "))); +} + +TEST(InterpretStringCapabilityTest, PopChar) { + EXPECT_THAT(Interpret("%p1%:-10c", {'x'}).terms, + ElementsAre(PointsToEmitBytes("x "))); +} + +TEST(InterpretStringCapabilityTest, StoreRecall) { + EXPECT_THAT(Interpret("%p1%Pa%p2%Pb%gb%s,%ga%d", {1, "foo"}).terms, + ElementsAre(PointsToEmitBytes("foo,1"))); +} + +TEST(InterpretStringCapabilityTest, PushChar) { + EXPECT_THAT(Interpret("%'x'%'y'%c%c").terms, + ElementsAre(PointsToEmitBytes("yx"))); +} + +TEST(InterpretStringCapabilityTest, PushNumber) { + EXPECT_THAT(Interpret("%{123}%d").terms, + ElementsAre(PointsToEmitBytes("123"))); +} + +TEST(InterpretStringCapabilityTest, Strlen) { + EXPECT_THAT(Interpret("%p1%l%d", {"foobar"}).terms, + ElementsAre(PointsToEmitBytes("6"))); +} + +TEST(InterpretStringCapabilityTest, Add) { + EXPECT_THAT(Interpret("%{5}%{2}%+%d").terms, + ElementsAre(PointsToEmitBytes("7"))); +} + +TEST(InterpretStringCapabilityTest, Subtract) { + EXPECT_THAT(Interpret("%{5}%{2}%-%d").terms, + ElementsAre(PointsToEmitBytes("3"))); +} + +TEST(InterpretStringCapabilityTest, Multiply) { + EXPECT_THAT(Interpret("%{5}%{2}%*%d").terms, + ElementsAre(PointsToEmitBytes("10"))); +} + +TEST(InterpretStringCapabilityTest, Divide) { + EXPECT_THAT(Interpret("%{5}%{2}%/%d").terms, + ElementsAre(PointsToEmitBytes("2"))); +} + +TEST(InterpretStringCapabilityTest, Modulo) { + EXPECT_THAT(Interpret("%{5}%{2}%m%d").terms, + ElementsAre(PointsToEmitBytes("1"))); +} + +TEST(InterpretStringCapabilityTest, BitwiseAnd) { + EXPECT_THAT(Interpret("%{3}%{15}%&%d").terms, + ElementsAre(PointsToEmitBytes("3"))); +} + +TEST(InterpretStringCapabilityTest, BitwiseOr) { + EXPECT_THAT(Interpret("%{2}%{1}%|%d").terms, + ElementsAre(PointsToEmitBytes("3"))); +} + +TEST(InterpretStringCapabilityTest, BitwiseXor) { + EXPECT_THAT(Interpret("%{15}%{10}%^%d").terms, + ElementsAre(PointsToEmitBytes("5"))); +} + +TEST(InterpretStringCapabilityTest, Equals) { + EXPECT_THAT(Interpret("%{15}%{15}%=%d").terms, + ElementsAre(PointsToEmitBytes("1"))); + EXPECT_THAT(Interpret("%{15}%{14}%=%d").terms, + ElementsAre(PointsToEmitBytes("0"))); +} + +TEST(InterpretStringCapabilityTest, LessThan) { + EXPECT_THAT(Interpret("%{2}%{3}%<%d").terms, + ElementsAre(PointsToEmitBytes("1"))); + EXPECT_THAT(Interpret("%{2}%{2}%<%d").terms, + ElementsAre(PointsToEmitBytes("0"))); +} + +TEST(InterpretStringCapabilityTest, GreaterThan) { + EXPECT_THAT(Interpret("%{3}%{2}%>%d").terms, + ElementsAre(PointsToEmitBytes("1"))); + EXPECT_THAT(Interpret("%{2}%{2}%>%d").terms, + ElementsAre(PointsToEmitBytes("0"))); +} + +TEST(InterpretStringCapabilityTest, LogicalAnd) { + EXPECT_THAT(Interpret("%{0}%{0}%A%d").terms, + ElementsAre(PointsToEmitBytes("0"))); + EXPECT_THAT(Interpret("%{0}%{1}%A%d").terms, + ElementsAre(PointsToEmitBytes("0"))); + EXPECT_THAT(Interpret("%{1}%{0}%A%d").terms, + ElementsAre(PointsToEmitBytes("0"))); + EXPECT_THAT(Interpret("%{1}%{1}%A%d").terms, + ElementsAre(PointsToEmitBytes("1"))); +} + +TEST(InterpretStringCapabilityTest, LogicalOr) { + EXPECT_THAT(Interpret("%{0}%{0}%O%d").terms, + ElementsAre(PointsToEmitBytes("0"))); + EXPECT_THAT(Interpret("%{0}%{1}%O%d").terms, + ElementsAre(PointsToEmitBytes("1"))); + EXPECT_THAT(Interpret("%{1}%{0}%O%d").terms, + ElementsAre(PointsToEmitBytes("1"))); + EXPECT_THAT(Interpret("%{1}%{1}%O%d").terms, + ElementsAre(PointsToEmitBytes("1"))); +} + +TEST(InterpretStringCapabilityTest, LogicalNot) { + EXPECT_THAT(Interpret("%{0}%!%d").terms, ElementsAre(PointsToEmitBytes("1"))); + EXPECT_THAT(Interpret("%{5}%!%d").terms, ElementsAre(PointsToEmitBytes("0"))); +} + +TEST(InterpretStringCapabilityTest, BitwiseNot) { + EXPECT_THAT(Interpret("%{8}%~%d").terms, + ElementsAre(PointsToEmitBytes("-9"))); +} + +TEST(InterpretStringCapabilityTest, IncrementParameters) { + EXPECT_THAT(Interpret("%i%p1%d,%p2%d", {6, 8}).terms, + ElementsAre(PointsToEmitBytes("7,9"))); +} + +TEST(InterpretStringCapabilityTest, IfThen) { + EXPECT_THAT(Interpret("%?%{0}%ttrue%;").terms, IsEmpty()); + EXPECT_THAT(Interpret("%?%{1}%ttrue%;").terms, + ElementsAre(PointsToEmitBytes("true"))); +} + +TEST(InterpretStringCapabilityTest, IfThenElse) { + EXPECT_THAT(Interpret("%?%{0}%ttrue%efalse%;").terms, + ElementsAre(PointsToEmitBytes("false"))); + EXPECT_THAT(Interpret("%?%{1}%ttrue%efalse%;").terms, + ElementsAre(PointsToEmitBytes("true"))); +} + +TEST(InterpretStringCapabilityTest, IfThenElseThen) { + EXPECT_THAT(Interpret("%?%{0}%t!%e%{1}%tgood%;").terms, + ElementsAre(PointsToEmitBytes("good"))); +} + +TEST(InterpretStringCapabilityTest, IfThenElseThenElse) { + EXPECT_THAT(Interpret("%?%{0}%t!%e%{0}%t!%egood%;").terms, + ElementsAre(PointsToEmitBytes("good"))); +} + +TEST(InterpretStringCapabilityTest, IfThenElseThenElseShortCircuit) { + EXPECT_THAT(Interpret("%?%{1}%tgood%e%{0}%t!%e!%;").terms, + ElementsAre(PointsToEmitBytes("good"))); +} + +} // namespace +} // namespace goldfishterm_internal |