aboutsummaryrefslogtreecommitdiff
path: root/goldfishterm/terminfo_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'goldfishterm/terminfo_test.cc')
-rw-r--r--goldfishterm/terminfo_test.cc228
1 files changed, 228 insertions, 0 deletions
diff --git a/goldfishterm/terminfo_test.cc b/goldfishterm/terminfo_test.cc
new file mode 100644
index 0000000..c50cb90
--- /dev/null
+++ b/goldfishterm/terminfo_test.cc
@@ -0,0 +1,228 @@
+// 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/terminfo.h"
+
+#include <errno.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <fstream>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include <system_error>
+
+#include "third_party/abseil/absl/cleanup/cleanup.h"
+#include "third_party/abseil/absl/strings/str_cat.h"
+#include "third_party/abseil/absl/types/optional.h"
+
+namespace goldfishterm {
+namespace {
+
+using namespace std::literals::string_literals;
+
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+
+void SetEnv(const char* var, const char* val) {
+ if (setenv(var, val, /*overwrite=*/1)) {
+ throw std::system_error(errno, std::generic_category(), "setenv");
+ }
+}
+
+// Creates a temporary directory for this test.
+std::string MakeTemporaryDirectory() noexcept {
+ std::string dir =
+ absl::StrCat(testing::TempDir(), "/terminfo_test.", getpid());
+ std::filesystem::create_directories(dir);
+ return dir;
+}
+
+TEST(EntryParseTest, EmptyFails) {
+ std::istringstream s("");
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, BadMagicFails) {
+ std::istringstream s("foobar");
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, TooShortHeaderFails) {
+ std::istringstream s("\x1a\x01");
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, NegativeNamesLengthFails) {
+ std::istringstream s("\x1a\x01\xff\xff");
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, NegativeBooleansLengthFails) {
+ std::istringstream s("\x1a\x01\0\0\xff\xff"s);
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, NegativeNumbersCountFails) {
+ std::istringstream s("\x1a\x01\0\0\0\0\xff\xff"s);
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, NegativeStringsOffsetsCountFails) {
+ std::istringstream s("\x1a\x01\0\0\0\0\0\0\xff\xff"s);
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, NegativeStringsLengthFails) {
+ std::istringstream s("\x1a\x01\0\0\0\0\0\0\0\0\xff\xff"s);
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, MinimalTerminfo) {
+ std::istringstream s("\x1a\x01\0\0\0\0\0\0\0\0\0\0"s);
+ TerminfoEntry t(s);
+}
+
+TEST(EntryParseTest, NamesNotNullTerminated) {
+ std::istringstream s("\x1a\x01\x04\0\0\0\0\0\0\0\0\0term"s);
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, Names) {
+ std::istringstream s("\x1a\x01\x0f\0\0\0\0\0\0\0\0\0name|othername\0"s);
+ EXPECT_THAT(TerminfoEntry(s).names(), ElementsAre("name", "othername"));
+}
+
+TEST(EntryParseTest, FirstName) {
+ std::istringstream s("\x1a\x01\x0f\0\0\0\0\0\0\0\0\0name|othername\0"s);
+ EXPECT_EQ(TerminfoEntry(s).name(), "name");
+}
+
+TEST(EntryParseTest, Booleans) {
+ std::istringstream s("\x1a\x01\x01\0\x04\0\0\0\0\0\0\0\0\x01\x00\x01\x00"s);
+ TerminfoEntry t(s);
+ EXPECT_TRUE(t.get(BooleanCapability::kAutoLeftMargin));
+ EXPECT_FALSE(t.get(BooleanCapability::kAutoRightMargin));
+ EXPECT_TRUE(t.get(BooleanCapability::kNoEscCtlc));
+ EXPECT_FALSE(t.get(BooleanCapability::kCeolStandoutGlitch));
+}
+
+TEST(EntryParseTest, BooleanNegativeOneIsFalse) {
+ std::istringstream s("\x1a\x01\x01\0\x01\0\0\0\0\0\0\0\0\xff"s);
+ EXPECT_FALSE(TerminfoEntry(s).get(BooleanCapability::kAutoLeftMargin));
+}
+
+TEST(EntryParseTest, BooleanNegativeTwoIsFalse) {
+ std::istringstream s("\x1a\x01\x01\0\x01\0\0\0\0\0\0\0\0\xfe"s);
+ EXPECT_FALSE(TerminfoEntry(s).get(BooleanCapability::kAutoLeftMargin));
+}
+
+TEST(EntryParseTest, Numbers) {
+ std::istringstream s("\x1a\x01\x01\0\0\0\x02\0\0\0\0\0\0\0\x01\x02\x03\x04"s);
+ TerminfoEntry t(s);
+ EXPECT_EQ(t.get(NumericCapability::kColumns), 0x0201);
+ EXPECT_EQ(t.get(NumericCapability::kInitTabs), 0x0403);
+}
+
+TEST(EntryParseTest, PaddingBeforeNumbersForAlignment) {
+ std::istringstream s("\x1a\x01\x01\0\0\0\x02\0\0\0\0\0\0\x01\x02\x03\x04"s);
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, NumberNegativeOneOkay) {
+ std::istringstream s("\x1a\x01\x01\0\0\0\x01\0\0\0\0\0\0\0\xff\xff"s);
+ TerminfoEntry t(s);
+}
+
+TEST(EntryParseTest, NumberNegativeTwoOkay) {
+ std::istringstream s("\x1a\x01\x01\0\0\0\x01\0\0\0\0\0\0\0\xfe\xff"s);
+ TerminfoEntry t(s);
+}
+
+TEST(EntryParseTest, NumberNegativeThreeFails) {
+ std::istringstream s("\x1a\x01\x01\0\0\0\x01\0\0\0\0\0\0\0\xfd\xff"s);
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, ExtendedNumberFormat) {
+ std::istringstream s("\x1e\x02\x01\0\0\0\x01\0\0\0\0\0\0\0\x01\x02\x03\x04"s);
+ EXPECT_EQ(TerminfoEntry(s).get(NumericCapability::kColumns), 0x04030201);
+}
+
+TEST(EntryParseTest, StringsWithoutOffsets) {
+ std::istringstream s("\x1a\x01\x01\0\0\0\0\0\0\0\x08\0\0\0foo\0bar\0"s);
+ TerminfoEntry t(s);
+ EXPECT_EQ(t.get(StringCapability::kBackTab), absl::nullopt);
+ EXPECT_EQ(t.get(StringCapability::kBell), absl::nullopt);
+}
+
+TEST(EntryParseTest, ZeroOffsets) {
+ std::istringstream s("\x1a\x01\x01\0\0\0\0\0\x02\0\0\0\0\0\0\0\0\0"s);
+ TerminfoEntry t(s);
+ EXPECT_THAT(t.get(StringCapability::kBackTab), Optional(IsEmpty()));
+ EXPECT_THAT(t.get(StringCapability::kBell), Optional(IsEmpty()));
+}
+
+TEST(EntryParseTest, NegativeOneOffsetOkay) {
+ std::istringstream s("\x1a\x01\x01\0\0\0\0\0\x01\0\0\0\0\0\xff\xff"s);
+ EXPECT_EQ(TerminfoEntry(s).get(StringCapability::kBackTab), absl::nullopt);
+}
+
+TEST(EntryParseTest, NegativeTwoOffsetOkay) {
+ std::istringstream s("\x1a\x01\x01\0\0\0\0\0\x01\0\0\0\0\0\xfe\xff"s);
+ EXPECT_EQ(TerminfoEntry(s).get(StringCapability::kBackTab), absl::nullopt);
+}
+
+TEST(EntryParseTest, NegativeThreeOffsetFails) {
+ std::istringstream s("\x1a\x01\x01\0\0\0\0\0\x01\0\0\0\0\0\xfd\xff"s);
+ EXPECT_THROW(TerminfoEntry t(s), std::runtime_error);
+}
+
+TEST(EntryParseTest, Strings) {
+ std::istringstream s(
+ "\x1a\x01\x01\0\0\0\0\0\x02\0\x08\0\0\0\x04\0\0\0foo\0bar\0"s);
+ TerminfoEntry t(s);
+ EXPECT_THAT(t.get(StringCapability::kBackTab), Optional(Eq("bar")));
+ EXPECT_THAT(t.get(StringCapability::kBell), Optional(Eq("foo")));
+}
+
+TEST(SystemTerminfoTest, NoSuchTerminal) {
+ std::string tmp = MakeTemporaryDirectory();
+ absl::Cleanup remove_tmp = [&] { std::filesystem::remove_all(tmp); };
+ SetEnv("TERMINFO", tmp.c_str());
+ EXPECT_THROW(TerminfoEntry::FromSystemDatabase("vt100"), std::runtime_error);
+}
+
+TEST(SystemTerminfoTest, LooksUpAdm3a) {
+ std::string tmp = MakeTemporaryDirectory();
+ absl::Cleanup remove_tmp = [&] { std::filesystem::remove_all(tmp); };
+ SetEnv("TERMINFO", tmp.c_str());
+
+ std::filesystem::create_directories(absl::StrCat(tmp, "/n"));
+ std::string binary_terminfo =
+ "\x1a\x01\x11\0\0\0\0\0\0\0\0\0notarealterminal\0"s;
+ std::ofstream(std::string(absl::StrCat(tmp, "/n/notarealterminal")))
+ .write(binary_terminfo.data(), binary_terminfo.size());
+
+ EXPECT_EQ(TerminfoEntry::FromSystemDatabase("notarealterminal").name(),
+ "notarealterminal");
+}
+
+} // namespace
+} // namespace goldfishterm