blob: 1d19bb1e762cb1bef51d9deb6f3a9b949ba5e172 [file] [log] [blame]
/*
* Copyright (C) 2019 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 <binder/app_launch_event.h>
#include <gtest/gtest.h>
// TODO: move to app_launch_event.h
#include <google/protobuf/util/message_differencer.h>
using namespace iorap::binder; // NOLINT
using android::Parcel;
using Type = AppLaunchEvent::Type;
using Temperature = AppLaunchEvent::Temperature;
namespace iorap::binder {
inline bool ProtosEqual(const ::google::protobuf::Message& lhs,
const ::google::protobuf::Message& rhs) {
return ::google::protobuf::util::MessageDifferencer::Equals(lhs, rhs);
}
inline bool ProtosEqual(const ::google::protobuf::MessageLite& lhs,
const ::google::protobuf::MessageLite& rhs) {
// MessageLite does not support 'MessageDifferencer' which requires protobuf-full
// because it uses reflection.
//
// Serialize as a string and compare. This may lead to false inequality when protobufs
// are actually the same but their encodings are slightly different.
return lhs.GetTypeName() == rhs.GetTypeName()
&& lhs.SerializeAsString() == rhs.SerializeAsString();
}
template <typename T>
inline bool ProtoPointersEqual(const T& lhs_ptr, const T& rhs_ptr) {
if (lhs_ptr == nullptr && rhs_ptr == nullptr) {
return true;
}
else if (lhs_ptr != nullptr && rhs_ptr != nullptr) {
return ProtosEqual(*lhs_ptr, *rhs_ptr);
}
return false;
}
// Field-by-field equality.
// Protos are compared according by checking that their serialized encodings are the same.
inline bool operator==(const AppLaunchEvent& lhs, const AppLaunchEvent& rhs) {
# define EQ_OR_RETURN(l, r, val) if (!(l.val == r.val)) { return false; }
# define PROTO_EQ_OR_RETURN(l, r, val) if (!ProtoPointersEqual(l.val, r.val)) { return false; }
EQ_OR_RETURN(lhs, rhs, type);
EQ_OR_RETURN(lhs, rhs, sequence_id);
PROTO_EQ_OR_RETURN(lhs, rhs, intent_proto);
EQ_OR_RETURN(lhs, rhs, temperature);
PROTO_EQ_OR_RETURN(lhs, rhs, activity_record_proto);
# undef EQ_OR_RETURN
# undef PROTO_EQ_OR_RETURN
return true;
}
inline bool operator!=(const AppLaunchEvent& lhs, const AppLaunchEvent& rhs) {
return !(lhs == rhs);
}
static AppLaunchEvent MakeIntentStarted(int64_t sequence_id,
// non-null
std::unique_ptr<IntentProto> intent_proto) {
DCHECK(intent_proto != nullptr);
AppLaunchEvent e{Type::kIntentStarted, sequence_id, std::move(intent_proto)};
return e;
}
static AppLaunchEvent MakeIntentFailed(int64_t sequence_id) {
AppLaunchEvent e{Type::kIntentFailed, sequence_id};
return e;
}
static AppLaunchEvent
MakeActivityLaunched(int64_t sequence_id,
Temperature temperature,
// non-null
std::unique_ptr<ActivityRecordProto> activity_record_proto) {
DCHECK(activity_record_proto != nullptr);
AppLaunchEvent e{Type::kActivityLaunched,
sequence_id,
/*intent_proto*/nullptr,
temperature,
std::move(activity_record_proto)};
return e;
}
static AppLaunchEvent
MakeActivityLaunchCancelled(int64_t sequence_id,
// nullable
std::unique_ptr<ActivityRecordProto> activity_record_proto = nullptr) {
AppLaunchEvent e{Type::kActivityLaunchCancelled,
sequence_id,
/*intent_proto*/nullptr,
Temperature::kUninitialized,
std::move(activity_record_proto)};
return e;
}
static AppLaunchEvent
MakeActivityLaunchFinished(int64_t sequence_id,
// non-null
std::unique_ptr<ActivityRecordProto> activity_record_proto) {
DCHECK(activity_record_proto != nullptr);
AppLaunchEvent e{Type::kActivityLaunchFinished,
sequence_id,
/*intent_proto*/nullptr,
Temperature::kUninitialized,
std::move(activity_record_proto)};
return e;
}
} // namespace iorap::binder
auto MakeDummyIntent(std::string action = "package_name/.ClassName") {
std::unique_ptr<IntentProto> dummy_intent{new IntentProto{}};
dummy_intent->set_action(action);
return dummy_intent;
}
auto MakeDummyActivityRecord(std::string title = "package_name/.ClassName") {
std::unique_ptr<ActivityRecordProto> dummy{new ActivityRecordProto{}};
dummy->mutable_identifier()->set_title(title);
return dummy;
}
TEST(AppLaunchEventTest, Equals) {
EXPECT_EQ(MakeIntentStarted(456, MakeDummyIntent()), MakeIntentStarted(456, MakeDummyIntent()));
EXPECT_NE(MakeIntentStarted(45, MakeDummyIntent()), MakeIntentStarted(45, MakeDummyIntent("a")));
EXPECT_EQ(MakeIntentFailed(123), MakeIntentFailed(123));
EXPECT_NE(MakeIntentFailed(0), MakeIntentFailed(123));
EXPECT_EQ((MakeActivityLaunched(3, Temperature::kHot, MakeDummyActivityRecord())),
(MakeActivityLaunched(3, Temperature::kHot, MakeDummyActivityRecord())));
EXPECT_NE((MakeActivityLaunched(3, Temperature::kHot, MakeDummyActivityRecord())),
(MakeActivityLaunched(3, Temperature::kCold, MakeDummyActivityRecord())));
EXPECT_NE((MakeActivityLaunched(3, Temperature::kHot, MakeDummyActivityRecord())),
(MakeActivityLaunched(3, Temperature::kHot, MakeDummyActivityRecord("other title"))));
EXPECT_EQ((MakeActivityLaunchCancelled(4)),
(MakeActivityLaunchCancelled(4)));
EXPECT_EQ((MakeActivityLaunchCancelled(4, MakeDummyActivityRecord())),
(MakeActivityLaunchCancelled(4, MakeDummyActivityRecord())));
EXPECT_NE((MakeActivityLaunchCancelled(4, MakeDummyActivityRecord())),
(MakeActivityLaunchCancelled(4, MakeDummyActivityRecord("other"))));
EXPECT_NE((MakeActivityLaunchCancelled(4, MakeDummyActivityRecord())),
(MakeActivityLaunchCancelled(4)));
EXPECT_EQ((MakeActivityLaunchFinished(5, MakeDummyActivityRecord())),
(MakeActivityLaunchFinished(5, MakeDummyActivityRecord())));
EXPECT_NE((MakeActivityLaunchFinished(5, MakeDummyActivityRecord())),
(MakeActivityLaunchFinished(5, MakeDummyActivityRecord("other title"))));
}
template <typename T>
T ValueParcelRoundTrip(const T& value) {
::android::Parcel p;
CHECK_EQ(value.writeToParcel(&p), ::android::NO_ERROR);
T new_value;
p.setDataPosition(0);
CHECK_EQ(new_value.readFromParcel(&p), ::android::NO_ERROR);
return new_value;
}
#define EXPECT_PARCELING_ROUND_TRIP(a) EXPECT_EQ((a), ValueParcelRoundTrip((a)))
TEST(AppLaunchEventTest, ParcelingRoundTrip) {
EXPECT_PARCELING_ROUND_TRIP(MakeIntentStarted(456, MakeDummyIntent()));
EXPECT_PARCELING_ROUND_TRIP(MakeIntentFailed(123));
EXPECT_PARCELING_ROUND_TRIP(MakeActivityLaunched(3, Temperature::kHot, MakeDummyActivityRecord()));
EXPECT_PARCELING_ROUND_TRIP(MakeActivityLaunchCancelled(4));
EXPECT_PARCELING_ROUND_TRIP(MakeActivityLaunchCancelled(4, MakeDummyActivityRecord()));
EXPECT_PARCELING_ROUND_TRIP(MakeActivityLaunchFinished(5, MakeDummyActivityRecord()));
}