android_system_core/adb/transport_test.cpp
David Pursell d2b588e239 adb: fix adbd feature parsing for no features.
Previously the transport features list was only overwritten if a
new feature list was found. However, adbd can reuse the same atransport
object even if the adb server is killed and restarted, so the feature
list was not cleared properly if the newly started adb server didn't
provide one.

This CL fixes the bug by clearing the transport features list whenever
a connection banner is parsed.

Bug: http://b/24405971
Change-Id: Ia6ee6c9a46a621534681f6d4d7df77156b885eb9
2015-09-25 13:10:17 -07:00

221 lines
6.3 KiB
C++

/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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
*
* http://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 "transport.h"
#include <gtest/gtest.h>
#include "adb.h"
class TestTransport : public atransport {
public:
bool operator==(const atransport& rhs) const {
EXPECT_EQ(read_from_remote, rhs.read_from_remote);
EXPECT_EQ(write_to_remote, rhs.write_to_remote);
EXPECT_EQ(close, rhs.close);
EXPECT_EQ(kick, rhs.kick);
EXPECT_EQ(fd, rhs.fd);
EXPECT_EQ(transport_socket, rhs.transport_socket);
EXPECT_EQ(
0, memcmp(&transport_fde, &rhs.transport_fde, sizeof(fdevent)));
EXPECT_EQ(ref_count, rhs.ref_count);
EXPECT_EQ(sync_token, rhs.sync_token);
EXPECT_EQ(connection_state, rhs.connection_state);
EXPECT_EQ(online, rhs.online);
EXPECT_EQ(type, rhs.type);
EXPECT_EQ(usb, rhs.usb);
EXPECT_EQ(sfd, rhs.sfd);
EXPECT_EQ(serial, rhs.serial);
EXPECT_EQ(product, rhs.product);
EXPECT_EQ(model, rhs.model);
EXPECT_EQ(device, rhs.device);
EXPECT_EQ(devpath, rhs.devpath);
EXPECT_EQ(adb_port, rhs.adb_port);
EXPECT_EQ(kicked, rhs.kicked);
EXPECT_EQ(key, rhs.key);
EXPECT_EQ(0, memcmp(token, rhs.token, TOKEN_SIZE));
EXPECT_EQ(0, memcmp(&auth_fde, &rhs.auth_fde, sizeof(fdevent)));
EXPECT_EQ(failed_auth_attempts, rhs.failed_auth_attempts);
EXPECT_EQ(features(), rhs.features());
return true;
}
};
class TransportSetup {
public:
TransportSetup() {
#ifdef _WIN32
// Use extern instead of including sysdeps.h which brings in various macros
// that conflict with APIs used in this file.
extern void adb_sysdeps_init(void);
adb_sysdeps_init();
#else
// adb_sysdeps_init() is an inline function that we cannot link against.
#endif
}
};
// Static initializer will call adb_sysdeps_init() before main() to initialize
// the transport mutex before it is used in the tests. Alternatives would be to
// use __attribute__((constructor)) here or to use that or a static initializer
// for adb_sysdeps_init() itself in sysdeps_win32.cpp (caveats of unclear
// init order), or to use a test fixture whose SetUp() could do the init once.
static TransportSetup g_TransportSetup;
TEST(transport, kick_transport) {
TestTransport t;
// Mutate some member so we can test that the function is run.
t.kick = [](atransport* trans) { trans->fd = 42; };
TestTransport expected;
expected.kick = t.kick;
expected.fd = 42;
expected.kicked = 1;
kick_transport(&t);
ASSERT_EQ(42, t.fd);
ASSERT_EQ(1, t.kicked);
ASSERT_EQ(expected, t);
}
TEST(transport, kick_transport_already_kicked) {
// Ensure that the transport is not modified if the transport has already been
// kicked.
TestTransport t;
t.kicked = 1;
t.kick = [](atransport*) { FAIL() << "Kick should not have been called"; };
TestTransport expected;
expected.kicked = 1;
expected.kick = t.kick;
kick_transport(&t);
ASSERT_EQ(expected, t);
}
static void DisconnectFunc(void* arg, atransport*) {
int* count = reinterpret_cast<int*>(arg);
++*count;
}
TEST(transport, RunDisconnects) {
atransport t;
// RunDisconnects() can be called with an empty atransport.
t.RunDisconnects();
int count = 0;
adisconnect disconnect;
disconnect.func = DisconnectFunc;
disconnect.opaque = &count;
t.AddDisconnect(&disconnect);
t.RunDisconnects();
ASSERT_EQ(1, count);
// disconnect should have been removed automatically.
t.RunDisconnects();
ASSERT_EQ(1, count);
count = 0;
t.AddDisconnect(&disconnect);
t.RemoveDisconnect(&disconnect);
t.RunDisconnects();
ASSERT_EQ(0, count);
}
TEST(transport, SetFeatures) {
atransport t;
ASSERT_EQ(0U, t.features().size());
t.SetFeatures(FeatureSetToString(FeatureSet{"foo"}));
ASSERT_EQ(1U, t.features().size());
ASSERT_TRUE(t.has_feature("foo"));
t.SetFeatures(FeatureSetToString(FeatureSet{"foo", "bar"}));
ASSERT_EQ(2U, t.features().size());
ASSERT_TRUE(t.has_feature("foo"));
ASSERT_TRUE(t.has_feature("bar"));
t.SetFeatures(FeatureSetToString(FeatureSet{"foo", "bar", "foo"}));
ASSERT_EQ(2U, t.features().size());
ASSERT_TRUE(t.has_feature("foo"));
ASSERT_TRUE(t.has_feature("bar"));
t.SetFeatures(FeatureSetToString(FeatureSet{"bar", "baz"}));
ASSERT_EQ(2U, t.features().size());
ASSERT_FALSE(t.has_feature("foo"));
ASSERT_TRUE(t.has_feature("bar"));
ASSERT_TRUE(t.has_feature("baz"));
t.SetFeatures("");
ASSERT_EQ(0U, t.features().size());
}
TEST(transport, parse_banner_no_features) {
atransport t;
parse_banner("host::", &t);
ASSERT_EQ(0U, t.features().size());
ASSERT_EQ(kCsHost, t.connection_state);
ASSERT_EQ(nullptr, t.product);
ASSERT_EQ(nullptr, t.model);
ASSERT_EQ(nullptr, t.device);
}
TEST(transport, parse_banner_product_features) {
atransport t;
const char banner[] =
"host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;";
parse_banner(banner, &t);
ASSERT_EQ(kCsHost, t.connection_state);
ASSERT_EQ(0U, t.features().size());
ASSERT_EQ(std::string("foo"), t.product);
ASSERT_EQ(std::string("bar"), t.model);
ASSERT_EQ(std::string("baz"), t.device);
}
TEST(transport, parse_banner_features) {
atransport t;
const char banner[] =
"host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;"
"features=woodly,doodly";
parse_banner(banner, &t);
ASSERT_EQ(kCsHost, t.connection_state);
ASSERT_EQ(2U, t.features().size());
ASSERT_TRUE(t.has_feature("woodly"));
ASSERT_TRUE(t.has_feature("doodly"));
ASSERT_EQ(std::string("foo"), t.product);
ASSERT_EQ(std::string("bar"), t.model);
ASSERT_EQ(std::string("baz"), t.device);
}