blob: 870b2f40addf977b400cbf7da9e6836946bf0464 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/installer/zucchini/zucchini_gen.h"
#include <stdint.h>
#include <utility>
#include <vector>
#include "chrome/installer/zucchini/equivalence_map.h"
#include "chrome/installer/zucchini/image_index.h"
#include "chrome/installer/zucchini/image_utils.h"
#include "chrome/installer/zucchini/label_manager.h"
#include "chrome/installer/zucchini/test_disassembler.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace zucchini {
namespace {
using OffsetVector = std::vector<offset_t>;
// In normal usage, 0.0 is an unrealistic similarity value for an
// EquivalenceCandiate. Since similarity doesn't affect results for various unit
// tests in this file, we use this dummy value for simplicity.
constexpr double kDummySim = 0.0;
// Helper function wrapping GenerateReferencesDelta().
std::vector<int32_t> GenerateReferencesDeltaTest(
const std::vector<Reference>& old_references,
const std::vector<Reference>& new_references,
std::vector<offset_t>&& exp_old_targets,
std::vector<offset_t>&& new_labels,
const EquivalenceMap& equivalence_map) {
ReferenceDeltaSink reference_delta_sink;
TargetPool old_targets;
old_targets.InsertTargets(old_references);
EXPECT_EQ(exp_old_targets, old_targets.targets());
ReferenceSet old_refs({1, TypeTag(0), PoolTag(0)}, old_targets);
old_refs.InitReferences(old_references);
TargetPool new_targets;
new_targets.InsertTargets(new_references);
ReferenceSet new_refs({1, TypeTag(0), PoolTag(0)}, new_targets);
new_refs.InitReferences(new_references);
UnorderedLabelManager label_manager;
label_manager.Init(std::move(new_labels));
GenerateReferencesDelta(old_refs, new_refs, label_manager, equivalence_map,
&reference_delta_sink);
// Serialize |reference_delta_sink| to patch format, and read it back as
// std::vector<int32_t>.
std::vector<uint8_t> buffer(reference_delta_sink.SerializedSize());
BufferSink sink(buffer.data(), buffer.size());
reference_delta_sink.SerializeInto(&sink);
BufferSource source(buffer.data(), buffer.size());
ReferenceDeltaSource reference_delta_source;
EXPECT_TRUE(reference_delta_source.Initialize(&source));
std::vector<int32_t> delta_vec;
for (auto delta = reference_delta_source.GetNext(); delta.has_value();
delta = reference_delta_source.GetNext()) {
delta_vec.push_back(*delta);
}
EXPECT_TRUE(reference_delta_source.Done());
return delta_vec;
}
// Helper function wrapping FindExtraTargets(). |new_references| take rvalue so
// callers can be simplified.
std::vector<offset_t> FindExtraTargetsTest(
std::vector<Reference>&& new_references,
std::vector<offset_t>&& new_labels,
EquivalenceMap&& equivalence_map) {
TargetPool new_targets;
new_targets.InsertTargets(new_references);
ReferenceSet reference_set({1, TypeTag(0), PoolTag(0)}, new_targets);
reference_set.InitReferences(new_references);
UnorderedLabelManager new_label_manager;
new_label_manager.Init(std::move(new_labels));
return FindExtraTargets(reference_set, new_label_manager, equivalence_map);
}
} // namespace
TEST(ZucchiniGenTest, MakeNewTargetsFromEquivalenceMap) {
// Note that |old_offsets| provided are sorted, and |equivalences| provided
// are sorted by |src_offset|.
constexpr auto kBad = kUnusedIndex;
EXPECT_EQ(OffsetVector(), MakeNewTargetsFromEquivalenceMap({}, {}));
EXPECT_EQ(OffsetVector({kBad, kBad}),
MakeNewTargetsFromEquivalenceMap({0, 1}, {}));
EXPECT_EQ(OffsetVector({0, 1, kBad}),
MakeNewTargetsFromEquivalenceMap({0, 1, 2}, {{0, 0, 2}}));
EXPECT_EQ(OffsetVector({1, 2, kBad}),
MakeNewTargetsFromEquivalenceMap({0, 1, 2}, {{0, 1, 2}}));
EXPECT_EQ(OffsetVector({1, kBad, 4, 5, 6, kBad}),
MakeNewTargetsFromEquivalenceMap({0, 1, 2, 3, 4, 5},
{{0, 1, 1}, {2, 4, 3}}));
EXPECT_EQ(OffsetVector({3, kBad, 0, 1, 2, kBad}),
MakeNewTargetsFromEquivalenceMap({0, 1, 2, 3, 4, 5},
{{0, 3, 1}, {2, 0, 3}}));
// Overlap in src.
EXPECT_EQ(OffsetVector({1, 2, 3, kBad, kBad}),
MakeNewTargetsFromEquivalenceMap({0, 1, 2, 3, 4},
{{0, 1, 3}, {1, 4, 2}}));
EXPECT_EQ(OffsetVector({1, 4, 5, 6, kBad}),
MakeNewTargetsFromEquivalenceMap({0, 1, 2, 3, 4},
{{0, 1, 2}, {1, 4, 3}}));
EXPECT_EQ(OffsetVector({1, 2, 5, kBad, kBad}),
MakeNewTargetsFromEquivalenceMap({0, 1, 2, 3, 4},
{{0, 1, 2}, {1, 4, 2}}));
// Jump in src.
EXPECT_EQ(OffsetVector({5, kBad, 6}),
MakeNewTargetsFromEquivalenceMap(
{10, 13, 15}, {{0, 1, 2}, {9, 4, 2}, {15, 6, 2}}));
// Tie-breaking: Prefer longest Equivalence, and then Equivalence with min
// |dst_offset|.
EXPECT_EQ(OffsetVector({11}),
MakeNewTargetsFromEquivalenceMap({1}, {{0, 10, 4}, {1, 21, 3}}));
EXPECT_EQ(OffsetVector({21}),
MakeNewTargetsFromEquivalenceMap({1}, {{0, 10, 3}, {1, 21, 4}}));
EXPECT_EQ(OffsetVector({11}),
MakeNewTargetsFromEquivalenceMap({1}, {{0, 10, 4}, {1, 21, 4}}));
}
TEST(ZucchiniGenTest, FindExtraTargets) {
// Note that |new_offsets| provided are sorted, and |equivalences| provided
// are sorted by |dst_offset|.
// No equivalences.
EXPECT_EQ(OffsetVector(), FindExtraTargetsTest({}, {}, {}));
EXPECT_EQ(OffsetVector(), FindExtraTargetsTest({{20, 0}}, {}, {}));
EXPECT_EQ(OffsetVector(),
FindExtraTargetsTest({{20, 0}}, {kUnusedIndex}, {}));
// Unrelated equivalence.
EXPECT_EQ(OffsetVector(),
FindExtraTargetsTest({{20, 0}, {23, 0}}, {},
EquivalenceMap({{{12, 21, 2}, kDummySim}})));
// Simple cases with one reference.
EXPECT_EQ(OffsetVector({1}),
FindExtraTargetsTest({{20, 1}}, {},
EquivalenceMap({{{10, 20, 2}, kDummySim}})));
EXPECT_EQ(OffsetVector({1}),
FindExtraTargetsTest({{20, 1}}, {kUnusedIndex},
EquivalenceMap({{{10, 20, 2}, kDummySim}})));
// With symmetry.
EXPECT_EQ(OffsetVector({1}),
FindExtraTargetsTest({{10, 1}}, {kUnusedIndex},
EquivalenceMap({{{10, 10, 2}, kDummySim}})));
// Target ignored because it is not "extra" (found as label).
EXPECT_EQ(OffsetVector({}),
FindExtraTargetsTest({{20, 1}}, {1},
EquivalenceMap({{{10, 20, 2}, kDummySim}})));
EXPECT_EQ(OffsetVector({}),
FindExtraTargetsTest({{20, 1}}, {2, 1},
EquivalenceMap({{{10, 20, 2}, kDummySim}})));
EXPECT_EQ(OffsetVector({}),
FindExtraTargetsTest({{20, 1}}, {kUnusedIndex, 1},
EquivalenceMap({{{10, 20, 2}, kDummySim}})));
// Simple cases with multiple references.
EXPECT_EQ(OffsetVector({5, 3}),
FindExtraTargetsTest({{20, 4}, {21, 5}, {22, 3}, {23, 6}}, {},
EquivalenceMap({{{10, 21, 2}, kDummySim}})));
// With unrelated new labels.
EXPECT_EQ(OffsetVector({5, 3}),
FindExtraTargetsTest({{20, 4}, {21, 5}, {22, 3}, {23, 6}},
{kUnusedIndex, 9},
EquivalenceMap({{{10, 21, 2}, kDummySim}})));
// With unrelated equivalences.
EXPECT_EQ(OffsetVector({5, 3}),
FindExtraTargetsTest({{20, 4}, {21, 5}, {22, 3}, {23, 6}}, {},
EquivalenceMap({{{11, 11, 2}, kDummySim},
{{10, 21, 2}, kDummySim}})));
EXPECT_EQ(OffsetVector({5, 3}),
FindExtraTargetsTest({{20, 4}, {21, 5}, {22, 3}, {23, 6}}, {},
EquivalenceMap({{{10, 21, 2}, kDummySim},
{{11, 31, 2}, kDummySim}})));
// Multiple equivalences. Targets are ordered for simplicity.
EXPECT_EQ(OffsetVector({1, 2, 4, 5}),
FindExtraTargetsTest(
{{20, 0}, {21, 1}, {22, 2}, {23, 3}, {24, 4}, {25, 5}, {26, 6}},
{kUnusedIndex},
EquivalenceMap(
{{{10, 21, 2}, kDummySim}, {{10, 24, 2}, kDummySim}})));
EXPECT_EQ(OffsetVector({2, 4}),
FindExtraTargetsTest(
{{20, 0}, {21, 1}, {22, 2}, {23, 3}, {24, 4}, {25, 5}, {26, 6}},
{5, kUnusedIndex, 1},
EquivalenceMap(
{{{10, 21, 2}, kDummySim}, {{10, 24, 2}, kDummySim}})));
EXPECT_EQ(OffsetVector({4, 5}),
FindExtraTargetsTest({{23, 3}, {24, 4}, {25, 5}, {26, 6}}, {},
EquivalenceMap({{{10, 21, 2}, kDummySim},
{{10, 24, 2}, kDummySim}})));
}
TEST(ZucchiniGenTest, GenerateReferencesDelta) {
// No equivalences.
EXPECT_EQ(std::vector<int32_t>(),
GenerateReferencesDeltaTest({}, {}, {}, {}, EquivalenceMap()));
EXPECT_EQ(std::vector<int32_t>(),
GenerateReferencesDeltaTest({{10, 0}}, {{20, 0}}, {0}, {},
EquivalenceMap()));
// Simple cases with one equivalence.
EXPECT_EQ(
std::vector<int32_t>({0}), // {0 - 0}.
GenerateReferencesDeltaTest({{10, 3}}, {{20, 3}}, {3}, {3, 4},
EquivalenceMap({{{10, 20, 4}, kDummySim}})));
EXPECT_EQ(
std::vector<int32_t>({1}), // {1 - 0}.
GenerateReferencesDeltaTest({{10, 3}}, {{20, 3}}, {3}, {4, 3},
EquivalenceMap({{{10, 20, 4}, kDummySim}})));
EXPECT_EQ(std::vector<int32_t>({2, -1}), // {2 - 0, 0 - 1}.
GenerateReferencesDeltaTest(
{{10, 3}, {11, 4}}, {{20, 3}, {21, 4}}, {3, 4}, {4, 5, 3},
EquivalenceMap({{{10, 20, 4}, kDummySim}})));
EXPECT_EQ(std::vector<int32_t>({0, 0}), // {1 - 1, 2 - 2}.
GenerateReferencesDeltaTest(
{{10, 3}, {11, 4}, {12, 5}, {13, 6}},
{{20, 3}, {21, 4}, {22, 5}, {23, 6}}, {3, 4, 5, 6},
{3, 4, 5, 6}, EquivalenceMap({{{11, 21, 2}, kDummySim}})));
// Multiple equivalences.
EXPECT_EQ(std::vector<int32_t>({-1, 1}), // {0 - 1, 1 - 0}.
GenerateReferencesDeltaTest(
{{10, 0}, {12, 1}}, {{10, 0}, {12, 1}}, {0, 1}, {0, 1},
EquivalenceMap(
{{{12, 10, 2}, kDummySim}, {{10, 12, 2}, kDummySim}})));
EXPECT_EQ(
std::vector<int32_t>({0, 0}), // {0 - 0, 1 - 1}.
GenerateReferencesDeltaTest(
{{0, 0}, {2, 1}}, {{0, 0}, {2, 1}}, {0, 1}, {1, 0},
EquivalenceMap({{{2, 0, 2}, kDummySim}, {{0, 2, 2}, kDummySim}})));
EXPECT_EQ(std::vector<int32_t>({-2, 2}), // {0 - 2, 2 - 0}.
GenerateReferencesDeltaTest(
{{10, 0}, {12, 1}, {14, 2}}, {{10, 0}, {12, 1}, {14, 2}},
{0, 1, 2}, {0, 1, 2},
EquivalenceMap(
{{{14, 10, 2}, kDummySim}, {{10, 14, 2}, kDummySim}})));
EXPECT_EQ(std::vector<int32_t>({-2, 2}), // {0 - 2, 2 - 0}.
GenerateReferencesDeltaTest(
{{11, 0}, {14, 1}, {17, 2}}, {{11, 0}, {14, 1}, {17, 2}},
{0, 1, 2}, {0, 1, 2},
EquivalenceMap(
{{{16, 10, 3}, kDummySim}, {{10, 16, 3}, kDummySim}})));
EXPECT_EQ(
std::vector<int32_t>({-2, 2}), // {0 - 2, 2 - 0}.
GenerateReferencesDeltaTest({{10, 0}, {14, 2}, {16, 1}},
{{10, 0}, {14, 2}}, {0, 1, 2}, {0, 1, 2},
EquivalenceMap({{{14, 10, 2}, kDummySim},
{{12, 12, 2}, kDummySim},
{{10, 14, 2}, kDummySim}})));
}
// TODO(huangs): Add more tests.
} // namespace zucchini