blob: 60b74eddd14d519d9621d0b5116de9cb621b8c78 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "typecd/port.h"
#include <string>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/stringprintf.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "typecd/test_constants.h"
#include "typecd/test_utils.h"
namespace {
constexpr char kInvalidDataRole1[] = "xsadft [hasdr]";
constexpr char kInvalidDataRole2[] = "]asdf[ dsdd";
constexpr char kValidDataRole1[] = "device";
constexpr char kValidDataRole2[] = "[host] device";
constexpr char kValidDataRole3[] = "host [device]";
constexpr char kValidPowerRole1[] = "[source] sink";
constexpr char kValidPowerRole2[] = "source [sink]";
constexpr char kInvalidPowerRole1[] = "asdf#//%sxdfa";
} // namespace
namespace typecd {
class PortTest : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
temp_dir_ = scoped_temp_dir_.GetPath();
}
public:
base::FilePath temp_dir_;
base::ScopedTempDir scoped_temp_dir_;
};
// Check that basic Port creation, partner addition/deletion works.
TEST_F(PortTest, TestBasicAdd) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
EXPECT_NE(nullptr, port);
port->AddPartner(base::FilePath(kFakePort0PartnerSysPath));
EXPECT_NE(nullptr, port->partner_);
port->RemovePartner();
EXPECT_EQ(nullptr, port->partner_);
}
// Check GetDataRole() for various sysfs values.
TEST_F(PortTest, TestGetDataRole) {
// Set up fake sysfs directory for the port..
auto port_path = temp_dir_.Append("port0");
ASSERT_TRUE(base::CreateDirectory(port_path));
auto data_role_path = port_path.Append("data_role");
ASSERT_TRUE(base::WriteFile(data_role_path, kValidDataRole1,
strlen(kValidDataRole1)));
// Create a port.
auto port = std::make_unique<Port>(base::FilePath(port_path), 0);
ASSERT_NE(nullptr, port);
EXPECT_EQ(DataRole::kDevice, port->GetDataRole());
ASSERT_TRUE(base::WriteFile(data_role_path, kValidDataRole2,
strlen(kValidDataRole2)));
// Fake a port changed event.
port->PortChanged();
EXPECT_EQ(DataRole::kHost, port->GetDataRole());
ASSERT_TRUE(base::WriteFile(port_path.Append("data_role"), kValidDataRole3,
strlen(kValidDataRole3)));
// Fake a port changed event.
port->PortChanged();
EXPECT_EQ(DataRole::kDevice, port->GetDataRole());
ASSERT_TRUE(base::WriteFile(port_path.Append("data_role"), kInvalidDataRole1,
strlen(kInvalidDataRole1)));
// Fake a port changed event.
port->PortChanged();
EXPECT_EQ(DataRole::kNone, port->GetDataRole());
ASSERT_TRUE(base::WriteFile(port_path.Append("data_role"), kInvalidDataRole2,
strlen(kInvalidDataRole2)));
// Fake a port changed event.
port->PortChanged();
EXPECT_EQ(DataRole::kNone, port->GetDataRole());
}
// Check GetPowerRole() for various sysfs values.
TEST_F(PortTest, TestGetPowerRole) {
// Set up fake sysfs directory for the port..
auto port_path = temp_dir_.Append("port0");
ASSERT_TRUE(base::CreateDirectory(port_path));
auto data_role_path = port_path.Append("power_role");
ASSERT_TRUE(base::WriteFile(data_role_path, kValidPowerRole1,
strlen(kValidPowerRole1)));
// Create a port.
auto port = std::make_unique<Port>(base::FilePath(port_path), 0);
ASSERT_NE(nullptr, port);
EXPECT_EQ(PowerRole::kSource, port->GetPowerRole());
ASSERT_TRUE(base::WriteFile(data_role_path, kValidPowerRole2,
strlen(kValidPowerRole2)));
// Fake a port changed event.
port->PortChanged();
EXPECT_EQ(PowerRole::kSink, port->GetPowerRole());
ASSERT_TRUE(base::WriteFile(data_role_path, kInvalidPowerRole1,
strlen(kInvalidPowerRole1)));
// Fake a port changed event.
port->PortChanged();
EXPECT_EQ(PowerRole::kNone, port->GetPowerRole());
}
// Check that DP Alt Mode Entry checks work as expected for a true case:
TEST_F(PortTest, TestDPAltModeEntryCheckTrue) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
port->AddPartner(base::FilePath(kFakePort0PartnerSysPath));
// Set up fake sysfs paths for 1 alt mode.
// Set the number of alt modes supported.
port->partner_->SetNumAltModes(1);
// Add the DP alt mode.
std::string mode0_dirname =
base::StringPrintf("port%d-partner.%d", 0, kDPAltModeIndex);
auto mode0_path = temp_dir_.Append(mode0_dirname);
ASSERT_TRUE(CreateFakeAltMode(mode0_path, kDPAltModeSID, kDPVDO_WD19TB,
kDPVDOIndex_WD19TB));
port->AddRemovePartnerAltMode(mode0_path, true);
AddAnkerUSB3p2Gen2Cable(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_FALSE(invalid_dpalt_cable);
}
// Check that DP Alt Mode Entry checks work as expected for a specific false
// case: The Startech dock DP VDO doesn't advertise DFP_D, so we *shouldn't*
// enter DP alternate mode, despite it supporting the DP SID.
TEST_F(PortTest, TestDPAltModeEntryCheckFalseWithDPSID) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddStartechDock(*port);
bool invalid_dpalt_cable = false;
EXPECT_FALSE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_FALSE(invalid_dpalt_cable);
}
// Check that DP Alt Mode Entry checks work as expected for false cases.
TEST_F(PortTest, TestDPAltModeEntryCheckFalse) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
port->AddPartner(base::FilePath(kFakePort0PartnerSysPath));
port->partner_->SetNumAltModes(0);
// Check the case where the partner doesn't support any alt modes.
EXPECT_FALSE(port->CanEnterDPAltMode(nullptr));
port->partner_->SetNumAltModes(1);
// Set up fake sysfs paths for 1 alt mode.
// Add the TBT alt mode.
std::string mode_dirname = base::StringPrintf("port%d-partner.%d", 0, 0);
auto mode_path = temp_dir_.Append(mode_dirname);
ASSERT_TRUE(
CreateFakeAltMode(mode_path, kTBTAltModeVID, kTBTVDO, kTBTVDOIndex));
port->AddRemovePartnerAltMode(mode_path, true);
EXPECT_FALSE(port->CanEnterDPAltMode(nullptr));
}
// Check that DP Alt Mode Entry works with cable check for a passing case.
// Case: The WIMAXIT Type-C display supports DP alternate mode and the CalDigit
// TBT4 cable supports up to USB4 so it should enter DP alternate mode and the
// cable will not be flagged as invalid
TEST_F(PortTest, TestDPAltModeEntryCalDigitTBT4ToDisplay) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddWimaxitDisplay(*port);
AddCalDigitTBT4Cable(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_FALSE(invalid_dpalt_cable);
}
// Check that DP Alt Mode Entry works with cable check for a passing case.
// Case: The WIMAXIT Type-C display supports DP alternate mode and the Anker
// USB3.2 Gen2 cable supports USB3 so it should enter DP alternate mode and
// the cable will not be flagged as invalid
TEST_F(PortTest, TestDPAltModeEntryAnkerUsb3Gen2ToDisplay) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddWimaxitDisplay(*port);
AddAnkerUSB3p2Gen2Cable(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_FALSE(invalid_dpalt_cable);
}
// Check that DP Alt Mode Entry works with cable check for a passing case.
// Case: The WIMAXIT Type-C display supports DP alternate mode and the HP
// USB3.2 Gen1 cable supports up to USB3.2 Gen1 so it should enter DP
// alternate mode and the cable will not be flagged as invalid
TEST_F(PortTest, TestDPAltModeEntryHPUsb3Gen1ToDisplay) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddWimaxitDisplay(*port);
AddHPUSB3p2Gen1Cable(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_FALSE(invalid_dpalt_cable);
}
// Check that DP Alt Mode Entry works with cable check for a passing case.
// Case: The WIMAXIT Type-C display supports DP alternate mode and the Apple
// TBT3 Pro cable supports up to USB4 so it should enter DP alternate mode
// and the cable will not be flagged as invalid
TEST_F(PortTest, TestDPAltModeEntryAppleTBT3ToDisplay) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddWimaxitDisplay(*port);
AddAppleTBT3ProCable(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_FALSE(invalid_dpalt_cable);
}
// Check that DP Alt Mode Entry works with cable check for a failing case.
// Case: The WIMAXIT Type-C display supports DP alternate mode but, an unbranded
// USB2 cable is not considered as a cable object in typecd. It should still try
// to enter alternate mode but the cable will be flagged as invalid
TEST_F(PortTest, TestDPAltModeEntryUnbrandedUSB2ToDisplay) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddWimaxitDisplay(*port);
AddUnbrandedUSB2Cable(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_TRUE(invalid_dpalt_cable);
}
// Check that DP Alt Mode Entry works with cable check for a failing case.
// Case: The WIMAXIT Type-C display supports DP alternate mode but, a tested
// Nekteck cable only supports up to USB2. The typec daemon should still try
// to enter alternate mode but the cable will be flagged as invalid
TEST_F(PortTest, TestDPAltModeEntryNekteckUSB2ToDisplay) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddWimaxitDisplay(*port);
AddNekteckUSB2PassiveCable(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_TRUE(invalid_dpalt_cable);
}
// Check that DP Alt Mode Entry works with cable check for a passing case.
// Case: The Thinkpad Dock supports DP alternate mode and a tested unbranded
// TBT3 cable supports up to USB3.2 Gen2 so it should enter DP alternate mode
// and the cable will not be flagged as invalid
TEST_F(PortTest, TestDPAltModeEntryTBT3ToDock) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddThinkpadTBT3Dock(*port);
AddUnbrandedTBT3Cable(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_FALSE(invalid_dpalt_cable);
}
// Check that DP Alt Mode Entry works with cable check for a failing case.
// Case: The Thinkpad Dock supports DP alternate mode but a tested unbranded
// USB2 cable is not recognized by the typec daemon. It should try to enter
// DP alternate mode but the cable will be flagged as invalid.
TEST_F(PortTest, TestDPAltModeEntryUnbrandedUSB2ToDock) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddThinkpadTBT3Dock(*port);
AddUnbrandedUSB2Cable(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_TRUE(invalid_dpalt_cable);
}
// Check that DP Alt Mode Entry works with cable check for a failing case.
// Case: The Thinkpad Dock supports DP alternate mode but a tested Nekteck
// type-c cable only supports up to USB2. The typec daemon should try to
// enter DP alternate mode but the cable will be flagged as invalid.
TEST_F(PortTest, TestDPAltModeEntryNekteckUSB2ToDock) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddThinkpadTBT3Dock(*port);
AddNekteckUSB2PassiveCable(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_TRUE(invalid_dpalt_cable);
}
// Check that DP Alt Mode Entry works with cable check for a passing case.
// Case: A small Cable Matters dock uses a captive cable. The type-c daemon
// will not recognize a cable for this dock, but because the partner notes it
// uses a captive cable typecd should enter DP Alt Mode without flagging the
// cable as invalid
TEST_F(PortTest, TestDPAltModeEntryCableMattersDock) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddCableMattersDock(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_FALSE(invalid_dpalt_cable);
}
// Check that TBT Compat Mode Entry checks work as expected for the following
// working case:
// - Startech.com TB3DK2DPW Alpine Ridge Dock.
// - StarTech Passive Cable 40 Gbps PD 2.0
TEST_F(PortTest, TestTBTCompatibilityModeEntryCheckTrueStartech) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddStartechTB3DK2DPWDock(*port);
AddStartech40GbpsCable(*port);
EXPECT_EQ(ModeEntryResult::kSuccess, port->CanEnterTBTCompatibilityMode());
}
// Check that TBT Compat Mode Entry checks work as expected for the following
// non-working case:
// - Startech.com TB3DK2DPW Alpine Ridge Dock.
// - Nekteck USB 2.0 cable (5A).
TEST_F(PortTest, TestTBTCompatibilityModeEntryCheckFalseStartech) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddStartechTB3DK2DPWDock(*port);
AddNekteckUSB2PassiveCable(*port);
EXPECT_EQ(ModeEntryResult::kCableError, port->CanEnterTBTCompatibilityMode());
}
// Check that TBT Compat Mode Entry checks work as expected for the following
// working case:
// - Dell WD19TB dock.
TEST_F(PortTest, TestTBTCompatibilityModeEntryCheckTrueWD19TB) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddDellWD19TBDock(*port);
EXPECT_EQ(ModeEntryResult::kSuccess, port->CanEnterTBTCompatibilityMode());
}
// Check that USB4 mode checks work as expected for the following
// working case:
// - Intel Gatkex Creek USB4 dock.
// - Belkin TBT3 Passive Cable 40Gbps.
TEST_F(PortTest, TestUSB4EntryTrueGatkexPassiveTBT3Cable) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddIntelUSB4GatkexCreekDock(*port);
AddBelkinTBT3PassiveCable(*port);
EXPECT_EQ(ModeEntryResult::kSuccess, port->CanEnterUSB4());
}
// Check that USB4 mode checks work as expected for the following
// working case:
// - Intel Gatkex Creek USB4 dock.
// - Hongju Full USB 3.1 Gen 1 5A passive cable..
TEST_F(PortTest, TestUSB4EntryTrueGatkexPassiveNonTBT3Cable) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddIntelUSB4GatkexCreekDock(*port);
AddHongjuUSB3p1Gen1Cable(*port);
EXPECT_EQ(ModeEntryResult::kSuccess, port->CanEnterUSB4());
}
// Check that USB4 mode checks work as expected for the following
// non-working case:
// - Intel Gatkex Creek USB4 dock.
// - Nekteck USB 2.0 5A Passive Cable.
TEST_F(PortTest, TestUSB4EntryFalseGatkexPassiveNonTBT3Cable) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddIntelUSB4GatkexCreekDock(*port);
AddNekteckUSB2PassiveCable(*port);
EXPECT_EQ(ModeEntryResult::kCableError, port->CanEnterUSB4());
}
// Check that USB4 mode checks work as expected for the following
// non-working case:
// - Intel Gatkex Creek USB4 dock.
// - Belkin TBT3 Active Cable 40Gbps.
//
// NOTE: This case is interesting because the TBT3 cable fails as it doesn't
// support Rounded Data rates.
TEST_F(PortTest, TestUSB4EntryFalseGatkexActiveTBT3Cable) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddIntelUSB4GatkexCreekDock(*port);
AddBelkinTBT3ActiveCable(*port);
EXPECT_EQ(ModeEntryResult::kCableError, port->CanEnterUSB4());
}
// Check that USB4 mode checks work as expected for the following
// working case:
// - Intel Gatkex Creek USB4 dock.
// - Apple Thunderbolt 3 Pro Cable.
TEST_F(PortTest, TestUSB4EntryTrueGatkexAppleTBT3ProCable) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddIntelUSB4GatkexCreekDock(*port);
AddAppleTBT3ProCable(*port);
EXPECT_EQ(ModeEntryResult::kSuccess, port->CanEnterUSB4());
}
// Check that USB4 device will enter TBT3 mode if the cable does not support
// USB4.
// Case: Thunderbolt 4 OWC dock connected with Belkin active TBT3 cable.
TEST_F(PortTest, TestUSB4ToTBT) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddOWCTBT4Dock(*port);
AddBelkinTBT3ActiveCable(*port);
EXPECT_EQ(ModeEntryResult::kCableError, port->CanEnterUSB4());
EXPECT_EQ(ModeEntryResult::kSuccess, port->CanEnterTBTCompatibilityMode());
}
// Check that USB4 device will enter DPAltMode if the cable does not support
// USB4 or TBT.
// Case: Thunderbolt 4 OWC dock connected with unbranded USB2 cable.
TEST_F(PortTest, TestUSB4ToDPAltMode) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddOWCTBT4Dock(*port);
AddUnbrandedUSB2Cable(*port);
bool invalid_dpalt_cable = false;
EXPECT_EQ(ModeEntryResult::kCableError, port->CanEnterUSB4());
EXPECT_EQ(ModeEntryResult::kCableError, port->CanEnterTBTCompatibilityMode());
// Cable is flagged as invalid, but typecd will still enter DPAltMode. For
// DPAltMode, the cable check is not a condition for mode entry.
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_TRUE(invalid_dpalt_cable);
}
// Check that CableLimitingUSBSpeed works for "false" case.
// Case: Thunderbolt 4 OWC dock connected with CalDigit Thunderbolt 4 cable.
TEST_F(PortTest, TestUSB4LimitedByCableFalse) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddOWCTBT4Dock(*port);
AddCalDigitTBT4Cable(*port);
EXPECT_EQ(ModeEntryResult::kSuccess, port->CanEnterUSB4());
EXPECT_FALSE(port->CableLimitingUSBSpeed());
}
// Check that CableLimitingUSBSpeed works for "true" case.
// Case: Thunderbolt 4 OWC dock connected with Cable Matters USB4 20Gbps cable.
TEST_F(PortTest, TestUSB4LimitedByCableTrue) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddOWCTBT4Dock(*port);
AddCableMatters20GbpsCable(*port);
EXPECT_EQ(ModeEntryResult::kSuccess, port->CanEnterUSB4());
EXPECT_TRUE(port->CableLimitingUSBSpeed());
}
// Check that CableLimitingUSBSpeed works for "false" case with passive TBT3
// (USB 3.2 Gen2) Cable.
// Case: Thunderbolt 4 OWC dock connected with unbranded TBT3 cable.
TEST_F(PortTest, TestUSB4LimitedByTBT3PassiveCableFalse) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddOWCTBT4Dock(*port);
AddUnbrandedTBT3Cable(*port);
EXPECT_EQ(ModeEntryResult::kSuccess, port->CanEnterUSB4());
EXPECT_FALSE(port->CableLimitingUSBSpeed());
}
// Check that CableLimitingUSBSpeed works for "false" case with passive TBT4
// (USB 3.2 Gen2) LRD Cable.
// Case: Thunderbolt 4 OWC dock connected with Cable Matters TBT4 LRD cable.
TEST_F(PortTest, TestUSB4LimitedByTBT4PassiveLRDCableFalse) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddOWCTBT4Dock(*port);
AddCableMattersTBT4LRDCable(*port);
EXPECT_EQ(ModeEntryResult::kSuccess, port->CanEnterUSB4());
EXPECT_FALSE(port->CableLimitingUSBSpeed());
}
// Check that CableLimitingUSBSpeed works for a case with AMA VDO.
// Case: WIMAXIT display connected with Anker USB 3.2 Gen2 cable.
TEST_F(PortTest, TestBillboardOnlyDisplayNotLimitedByCable) {
auto port = std::make_unique<Port>(base::FilePath(kFakePort0SysPath), 0);
AddWimaxitDisplay(*port);
AddAnkerUSB3p2Gen2Cable(*port);
bool invalid_dpalt_cable = false;
EXPECT_TRUE(port->CanEnterDPAltMode(&invalid_dpalt_cable));
EXPECT_FALSE(invalid_dpalt_cable);
EXPECT_FALSE(port->CableLimitingUSBSpeed());
}
} // namespace typecd