Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cefba61f authored by Steven Moreland's avatar Steven Moreland
Browse files

libbinder: binderParcelTest

For-now-minimal host-supported test to support testing Parcel wire
protocol on host. In the future, this may be expanded to test versions
of the libbinder wire protocol. This is useful because if there is a
minor mistake, when changing the wire protocol, the device will almost
inevitably end up in a bootloop, since binder is used to decrypt
partitions on the device and do other basic early setup.

Bug: 172502290
Test: binderParcelTest
Change-Id: Ie9ac9879bacb8eca3a6c07c8fc4998fe52d8f915
parent b1d6aaa8
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -15,6 +15,13 @@
    {
      "name": "binderTextOutputTest"
    },
    {
      "name": "binderParcelTest"
    },
    {
      "name": "binderParcelTest",
      "host": true
    },
    {
      "name": "binderLibTest"
    },
+17 −0
Original line number Diff line number Diff line
@@ -60,6 +60,23 @@ cc_test {
    require_root: true,
}

// unit test only, which can run on host and doesn't use /dev/binder
cc_test {
    name: "binderParcelTest",
    host_supported: true,
    target: {
        darwin: {
            enabled: false,
        },
    },
    srcs: ["binderParcelTest.cpp"],
    shared_libs: [
        "libbinder",
        "libutils",
    ],
    test_suites: ["general-tests"],
}

cc_test {
    name: "binderLibTest",
    defaults: ["binder_test_defaults"],
+114 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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/Parcel.h>
#include <binder/IPCThreadState.h>
#include <gtest/gtest.h>

using android::IPCThreadState;
using android::OK;
using android::Parcel;
using android::String16;
using android::String8;
using android::status_t;

// Tests a second operation results in a parcel at the same location as it
// started.
void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
    Parcel p;
    a(&p);
    size_t end = p.dataPosition();
    p.setDataPosition(0);
    b(&p);
    EXPECT_EQ(end, p.dataPosition());
}

TEST(Parcel, InverseInterfaceToken) {
    const String16 token = String16("asdf");
    parcelOpSameLength([&] (Parcel* p) {
        p->writeInterfaceToken(token);
    }, [&] (Parcel* p) {
        EXPECT_TRUE(p->enforceInterface(token, IPCThreadState::self()));
    });
}

TEST(Parcel, Utf8FromUtf16Read) {
    const char* token = "asdf";
    parcelOpSameLength([&] (Parcel* p) {
        p->writeString16(String16(token));
    }, [&] (Parcel* p) {
        std::string s;
        EXPECT_EQ(OK, p->readUtf8FromUtf16(&s));
        EXPECT_EQ(token, s);
    });
}

TEST(Parcel, Utf8AsUtf16Write) {
    std::string token = "asdf";
    parcelOpSameLength([&] (Parcel* p) {
        p->writeUtf8AsUtf16(token);
    }, [&] (Parcel* p) {
        String16 s;
        EXPECT_EQ(OK, p->readString16(&s));
        EXPECT_EQ(s, String16(token.c_str()));
    });
}

template <typename T>
using readFunc = status_t (Parcel::*)(T* out) const;
template <typename T>
using writeFunc = status_t (Parcel::*)(const T& in);
template <typename T>
using copyWriteFunc = status_t (Parcel::*)(T in);

template <typename T, typename WRITE_FUNC>
void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, WRITE_FUNC w) {
    for (const T& value : ts) {
        parcelOpSameLength([&] (Parcel* p) {
            (*p.*w)(value);
        }, [&] (Parcel* p) {
            T outValue;
            EXPECT_EQ(OK, (*p.*r)(&outValue));
            EXPECT_EQ(value, outValue);
        });
    }
}

template <typename T>
void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, writeFunc<T> w) {
    readWriteInverse<T, writeFunc<T>>(std::move(ts), r, w);
}
template <typename T>
void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, copyWriteFunc<T> w) {
    readWriteInverse<T, copyWriteFunc<T>>(std::move(ts), r, w);
}

#define TEST_READ_WRITE_INVERSE(type, name, ...) \
    TEST(Parcel, Inverse##name) { \
        readWriteInverse<type>(__VA_ARGS__, &Parcel::read##name, &Parcel::write##name); \
    }

TEST_READ_WRITE_INVERSE(int32_t, Int32, {-2, -1, 0, 1, 2});
TEST_READ_WRITE_INVERSE(uint32_t, Uint32, {0, 1, 2});
TEST_READ_WRITE_INVERSE(int64_t, Int64, {-2, -1, 0, 1, 2});
TEST_READ_WRITE_INVERSE(uint64_t, Uint64, {0, 1, 2});
TEST_READ_WRITE_INVERSE(float, Float, {-1.0f, 0.0f, 3.14f});
TEST_READ_WRITE_INVERSE(double, Double, {-1.0, 0.0, 3.14});
TEST_READ_WRITE_INVERSE(bool, Bool, {true, false});
TEST_READ_WRITE_INVERSE(char16_t, Char, {u'a', u'\0'});
TEST_READ_WRITE_INVERSE(int8_t, Byte, {-1, 0, 1});
TEST_READ_WRITE_INVERSE(String8, String8, {String8(), String8("a"), String8("asdf")});
TEST_READ_WRITE_INVERSE(String16, String16, {String16(), String16("a"), String16("asdf")});