// 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 #include #include #include #include #include #include #include #include #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) { start = s; length = strlen(s); } else { start = s.data(); length = s.size(); } return ExplainMatchResult(Pointee(WhenDynamicCastTo( 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(EmitDelay(t))), arg, result_listener); } InterpretStringCapabilityResult Interpret( std::string text, std::vector 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