| /* |
| * 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())); |
| } |