/*
* Copyright ( C ) 2021 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 <odr_compilation_log.h>
#include <time.h>
#include <cstdint>
#include <ctime>
#include <iosfwd>
#include <istream>
#include <limits>
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
#include "android-base/file.h"
#include "base/common_art_test.h"
#include "odrefresh/odrefresh.h"
#include "odr_metrics.h"
namespace art {
namespace odrefresh {
const time_t kSecondsPerDay = 86 '400;
class OdrCompilationLogTest : public CommonArtTest {};
TEST(OdrCompilationLogEntry, Equality) {
OdrCompilationLogEntry a{1 , 2 , 3 , 4 , 5 };
ASSERT_EQ(a, (OdrCompilationLogEntry{1 , 2 , 3 , 4 , 5 }));
ASSERT_NE(a, (OdrCompilationLogEntry{9 , 2 , 3 , 4 , 5 }));
ASSERT_NE(a, (OdrCompilationLogEntry{1 , 9 , 3 , 4 , 5 }));
ASSERT_NE(a, (OdrCompilationLogEntry{1 , 2 , 9 , 4 , 5 }));
ASSERT_NE(a, (OdrCompilationLogEntry{2 , 2 , 3 , 9 , 5 }));
ASSERT_NE(a, (OdrCompilationLogEntry{2 , 2 , 3 , 5 , 9 }));
}
TEST(OdrCompilationLogEntry, InputOutput) {
const OdrCompilationLogEntry entries[] = {
{1 , 2 , 3 , 4 , 5 },
{std::numeric_limits<int64_t>::min(),
std::numeric_limits<int64_t>::min(),
std::numeric_limits<int32_t>::min(),
std::numeric_limits<time_t>::min(),
std::numeric_limits<int32_t>::min()},
{std::numeric_limits<int64_t>::max(),
std::numeric_limits<int64_t>::max(),
std::numeric_limits<int32_t>::max(),
std::numeric_limits<time_t>::max(),
std::numeric_limits<int32_t>::max()},
{0 , 0 , 0 , 0 , 0 },
{0 x7fedcba9'87654321, 0x5a5a5a5a' 5 a5a5a5a, 0 x12345678, 0 x2346789, 0 x76543210}
};
for (const auto & entry : entries) {
std::stringstream ss;
ss << entry;
OdrCompilationLogEntry actual;
ss >> actual;
ASSERT_EQ(entry, actual);
}
}
TEST(OdrCompilationLogEntry, TruncatedInput) {
std::stringstream ss;
ss << "1 2" ;
OdrCompilationLogEntry entry;
ss >> entry;
ASSERT_TRUE(ss.fail());
ASSERT_FALSE(ss.bad());
}
TEST(OdrCompilationLogEntry, ReadMultiple) {
std::stringstream ss;
ss << "0 1 2 3 4\n5 6 7 8 9\n" ;
OdrCompilationLogEntry entry0, entry1;
ss >> entry0 >> entry1;
ASSERT_EQ(entry0, (OdrCompilationLogEntry{0 , 1 , 2 , 3 , 4 }));
ASSERT_EQ(entry1, (OdrCompilationLogEntry{5 , 6 , 7 , 8 , 9 }));
ASSERT_FALSE(ss.fail());
ASSERT_FALSE(ss.bad());
}
TEST(OdrCompilationLog, ShouldAttemptCompile) {
OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, 0 ));
ocl.Log(
/*apex_version=*/1,
/*last_update_millis=*/762,
OdrMetrics::Trigger::kApexVersionMismatch,
ExitCode::kCompilationFailed);
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kApexVersionMismatch));
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kDexFilesChanged));
ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown));
}
TEST(OdrCompilationLog, BackOffNoHistory) {
time_t start_time;
time(&start_time);
OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
// Start log
ocl.Log(/*apex_version=*/1,
/*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
ASSERT_FALSE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2 ));
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay));
// Add one more log entry
ocl.Log(/*apex_version=*/1,
/*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
ASSERT_FALSE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay));
ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 2 * kSecondsPerDay));
// One more.
ocl.Log(/*apex_version=*/1,
/*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
ASSERT_FALSE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 3 * kSecondsPerDay));
ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 4 * kSecondsPerDay));
// And one for the road.
ocl.Log(/*apex_version=*/1,
/*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
ASSERT_FALSE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 7 * kSecondsPerDay));
ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 8 * kSecondsPerDay));
}
TEST(OdrCompilationLog, BackOffHappyHistory) {
time_t start_time;
time(&start_time);
OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
// Start log with a successful entry.
ocl.Log(/*apex_version=*/1,
/*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationSuccess);
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 4 ));
ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2 ));
// Add a log entry for a failed compilation.
ocl.Log(/*apex_version=*/1,
/*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
ASSERT_FALSE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2 ));
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay));
}
TEST_F(OdrCompilationLogTest, LogNumberOfEntriesAndPeek) {
OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
std::vector<OdrCompilationLogEntry> entries = {
{ 0 , 1 , 2 , 3 , 4 },
{ 1 , 2 , 3 , 4 , 5 },
{ 2 , 3 , 4 , 5 , 6 },
{ 3 , 4 , 5 , 6 , 7 },
{ 4 , 5 , 6 , 7 , 8 },
{ 5 , 6 , 7 , 8 , 9 },
{ 6 , 7 , 8 , 9 , 10 }
};
for (size_t i = 0 ; i < entries.size(); ++i) {
OdrCompilationLogEntry& e = entries[i];
ocl.Log(e.apex_version,
e.last_update_millis,
static_cast <OdrMetrics::Trigger>(e.trigger),
e.when,
static_cast <ExitCode>(e.exit_code));
if (i < OdrCompilationLog::kMaxLoggedEntries) {
ASSERT_EQ(i + 1 , ocl.NumberOfEntries());
} else {
ASSERT_EQ(OdrCompilationLog::kMaxLoggedEntries, ocl.NumberOfEntries());
}
for (size_t j = 0 ; j < ocl.NumberOfEntries(); ++j) {
const OdrCompilationLogEntry* logged = ocl.Peek(j);
ASSERT_TRUE(logged != nullptr);
const OdrCompilationLogEntry& expected = entries[i + 1 - ocl.NumberOfEntries() + j];
ASSERT_EQ(expected, *logged);
}
}
}
TEST_F(OdrCompilationLogTest, LogReadWrite) {
std::vector<OdrCompilationLogEntry> entries = {
{ 0 , 1 , 2 , 3 , 4 },
{ 1 , 2 , 3 , 4 , 5 },
{ 2 , 3 , 4 , 5 , 6 },
{ 3 , 4 , 5 , 6 , 7 },
{ 4 , 5 , 6 , 7 , 8 },
{ 5 , 6 , 7 , 8 , 9 },
{ 6 , 7 , 8 , 9 , 10 }
};
ScratchFile scratch_file;
scratch_file.Close();
for (size_t i = 0 ; i < entries.size(); ++i) {
{
OdrCompilationLog ocl(scratch_file.GetFilename().c_str());
OdrCompilationLogEntry& e = entries[i];
ocl.Log(e.apex_version,
e.last_update_millis,
static_cast <OdrMetrics::Trigger>(e.trigger),
e.when,
static_cast <ExitCode>(e.exit_code));
}
{
OdrCompilationLog ocl(scratch_file.GetFilename().c_str());
if (i < OdrCompilationLog::kMaxLoggedEntries) {
ASSERT_EQ(i + 1 , ocl.NumberOfEntries());
} else {
ASSERT_EQ(OdrCompilationLog::kMaxLoggedEntries, ocl.NumberOfEntries());
}
for (size_t j = 0 ; j < ocl.NumberOfEntries(); ++j) {
const OdrCompilationLogEntry* logged = ocl.Peek(j);
ASSERT_TRUE(logged != nullptr);
const OdrCompilationLogEntry& expected = entries[i + 1 - ocl.NumberOfEntries() + j];
ASSERT_EQ(expected, *logged);
}
}
}
}
TEST_F(OdrCompilationLogTest, BackoffBasedOnLog) {
time_t start_time;
time(&start_time);
ScratchFile scratch_file;
scratch_file.Close();
const char * log_path = scratch_file.GetFilename().c_str();
{
OdrCompilationLog ocl(log_path);
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
}
{
OdrCompilationLog ocl(log_path);
// Start log
ocl.Log(/*apex_version=*/1,
/*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
}
{
OdrCompilationLog ocl(log_path);
ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
ASSERT_FALSE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2 ));
ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay));
}
{
// Add one more log entry
OdrCompilationLog ocl(log_path);
ocl.Log(/*apex_version=*/1,
/*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
}
{
OdrCompilationLog ocl(log_path);
ASSERT_FALSE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay));
ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 2 * kSecondsPerDay));
}
{
// One more log entry.
OdrCompilationLog ocl(log_path);
ocl.Log(/*apex_version=*/1,
/*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
}
{
OdrCompilationLog ocl(log_path);
ASSERT_FALSE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 3 * kSecondsPerDay));
ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 4 * kSecondsPerDay));
}
{
// And one for the road.
OdrCompilationLog ocl(log_path);
ocl.Log(/*apex_version=*/1,
/*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
}
{
OdrCompilationLog ocl(log_path);
ASSERT_FALSE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 7 * kSecondsPerDay));
ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 8 * kSecondsPerDay));
}
}
TEST_F(OdrCompilationLogTest, NewLogVersionTriggersCompilation) {
static const int64_t kApexVersion = 1066 ;
static const int64_t kLastUpdateMillis = 777 ;
time_t start_time;
time(&start_time);
ScratchFile scratch_file;
scratch_file.Close();
// Generate a compilation log.
{
OdrCompilationLog ocl(scratch_file.GetFilename().c_str());
for (size_t i = 0 ; i < OdrCompilationLog::kMaxLoggedEntries; ++i) {
ocl.Log(kApexVersion,
kLastUpdateMillis,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
}
}
// Replace version string in the compilation log.
std::string log_text;
ASSERT_TRUE(android::base::ReadFileToString(scratch_file.GetFilename(), &log_text));
std::string new_log_version = std::string(OdrCompilationLog::kLogVersion) + "a" ;
log_text.replace(0 , new_log_version.size() - 1 , new_log_version);
ASSERT_TRUE(android::base::WriteStringToFile(log_text, scratch_file.GetFilename()));
// Read log with updated version entry, check it is treated as out-of-date.
{
OdrCompilationLog ocl(scratch_file.GetFilename().c_str());
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
ASSERT_EQ(0 u, ocl.NumberOfEntries());
}
}
} // namespace odrefresh
} // namespace art
Messung V0.5 in Prozent C=88 H=94 G=90
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-29)
¤
*© Formatika GbR, Deutschland