aboutsummaryrefslogtreecommitdiff
path: root/goldfishterm/internal/string_capability_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'goldfishterm/internal/string_capability_test.cc')
-rw-r--r--goldfishterm/internal/string_capability_test.cc372
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