[debugger] Improve AddressRange.

Define Union to not expand when there is an empty range.

Add unit tests.

Mark possible functions as constexpr. This class is sometimes used to
hold constant data in tests.

Change-Id: I7ce7ea5d7395cbd23a8f8a32c44021a3336e1a69
diff --git a/src/developer/debug/debug_agent/debugged_process_unittest.cc b/src/developer/debug/debug_agent/debugged_process_unittest.cc
index 6404d93..c6c7d19 100644
--- a/src/developer/debug/debug_agent/debugged_process_unittest.cc
+++ b/src/developer/debug/debug_agent/debugged_process_unittest.cc
@@ -120,8 +120,7 @@
 constexpr uint64_t kAddress3 = 0x9abc;
 constexpr uint64_t kAddress4 = 0xdef0;
 
-const debug_ipc::AddressRange kAddressRange1 = {0x1, 0x2};
-const debug_ipc::AddressRange kAddressRange2 = {0x3, 0x4};
+constexpr debug_ipc::AddressRange kAddressRange1 = {0x1, 0x2};
 
 TEST(DebuggedProcess, RegisterBreakpoints) {
   MockProcessDelegate process_delegate;
diff --git a/src/developer/debug/shared/BUILD.gn b/src/developer/debug/shared/BUILD.gn
index 5655bd8..a4e6768 100644
--- a/src/developer/debug/shared/BUILD.gn
+++ b/src/developer/debug/shared/BUILD.gn
@@ -72,6 +72,7 @@
 source_set("tests") {
   testonly = true
   sources = [
+    "address_range_unittest.cc",
     "component_utils_unittest.cc",
     "message_loop_unittest.cc",
     "regex_unittest.cc",
diff --git a/src/developer/debug/shared/address_range.cc b/src/developer/debug/shared/address_range.cc
index a320b5a..b678865 100644
--- a/src/developer/debug/shared/address_range.cc
+++ b/src/developer/debug/shared/address_range.cc
@@ -6,16 +6,16 @@
 
 #include <inttypes.h>
 
-#include "src/lib/fxl/logging.h"
 #include "src/lib/fxl/strings/string_printf.h"
 
 namespace debug_ipc {
 
-AddressRange::AddressRange(uint64_t begin, uint64_t end) : begin_(begin), end_(end) {
-  FXL_DCHECK(end_ >= begin_);
-}
-
+// Implemented out-of-line to avoid bringing <algorithm> into all headers that use address_range.h.
 AddressRange AddressRange::Union(const AddressRange& other) const {
+  if (other.empty())
+    return *this;
+  if (empty())
+    return other;
   return AddressRange(std::min(begin_, other.begin_), std::max(end_, other.end_));
 }
 
diff --git a/src/developer/debug/shared/address_range.h b/src/developer/debug/shared/address_range.h
index 9beffb9..6220fb7 100644
--- a/src/developer/debug/shared/address_range.h
+++ b/src/developer/debug/shared/address_range.h
@@ -9,37 +9,46 @@
 
 #include <string>
 
+#include "src/lib/fxl/logging.h"
+
 namespace debug_ipc {
 
 class AddressRange {
  public:
-  AddressRange() = default;
-  AddressRange(uint64_t begin, uint64_t end);
+  constexpr AddressRange() = default;
+  constexpr AddressRange(uint64_t begin, uint64_t end) : begin_(begin), end_(end) {
+    FXL_DCHECK(end_ >= begin_);
+  }
 
-  uint64_t begin() const { return begin_; }
-  uint64_t end() const { return end_; }
+  constexpr uint64_t begin() const { return begin_; }
+  constexpr uint64_t end() const { return end_; }
 
-  uint64_t size() const { return end_ - begin_; }
-  bool empty() const { return end_ == begin_; }
+  constexpr uint64_t size() const { return end_ - begin_; }
+  constexpr bool empty() const { return end_ == begin_; }
 
-  bool InRange(uint64_t addr) const { return addr >= begin_ && addr < end_; }
+  constexpr bool InRange(uint64_t addr) const { return addr >= begin_ && addr < end_; }
 
-  bool Contains(const AddressRange& other) const {
+  // Callers need to consider the semantics they want for empty ranges.
+  //
+  // An empty range whose start and end are within this range is considered to Contain/Overlap
+  // this one. If you want to consider empty ranges as being unoverlapping with anything you will
+  // need to perform an extra check.
+  constexpr bool Contains(const AddressRange& other) const {
     return other.begin_ >= begin_ && other.end_ <= end_;
   }
-  bool Overlaps(const AddressRange& other) const {
+  constexpr bool Overlaps(const AddressRange& other) const {
     return other.begin_ < end_ && other.end_ >= begin_;
   }
 
   // Returns a new range covering both inputs (|this| and |other|). If the inputs don't touch, the
   // result will also cover the in-between addresses. Use the AddressRanges class if you need to
-  // represent multiple discontiguous ranges.
+  // represent multiple discontiguous ranges. Empty ranges do not count toward a union.
   [[nodiscard]] AddressRange Union(const AddressRange& other) const;
 
-  bool operator==(const AddressRange& other) const {
+  constexpr bool operator==(const AddressRange& other) const {
     return begin_ == other.begin_ && end_ == other.end_;
   }
-  bool operator!=(const AddressRange& other) const { return !operator==(other); }
+  constexpr bool operator!=(const AddressRange& other) const { return !operator==(other); }
 
   // Returns a string representing this set of ranges for debugging purposes.
   std::string ToString() const;
diff --git a/src/developer/debug/shared/address_range_unittest.cc b/src/developer/debug/shared/address_range_unittest.cc
new file mode 100644
index 0000000..6c5c95d
--- /dev/null
+++ b/src/developer/debug/shared/address_range_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright 2019 The Fuchsia 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 "src/developer/debug/shared/address_range.h"
+
+#include "gtest/gtest.h"
+
+namespace debug_ipc {
+
+TEST(AddressRange, InRange) {
+  constexpr AddressRange range(1, 5);
+  EXPECT_FALSE(range.InRange(0));
+  EXPECT_TRUE(range.InRange(1));
+  EXPECT_TRUE(range.InRange(4));
+  EXPECT_FALSE(range.InRange(5));
+}
+
+TEST(AddressRange, Contains) {
+  constexpr AddressRange range(100, 105);
+
+  // A range can contain itself.
+  EXPECT_TRUE(range.Contains(range));
+
+  // Completely inside.
+  EXPECT_TRUE(range.Contains(AddressRange(102, 104)));
+
+  // Completely outside.
+  EXPECT_FALSE(range.Contains(AddressRange(1, 99)));
+  EXPECT_FALSE(range.Contains(AddressRange(200, 205)));
+
+  // Partially overlapping.
+  EXPECT_FALSE(range.Contains(AddressRange(0, 102)));
+  EXPECT_FALSE(range.Contains(AddressRange(102, 200)));
+}
+
+TEST(AddressRange, Overlaps) {
+  constexpr AddressRange range(100, 105);
+
+  // A range can contain itself.
+  EXPECT_TRUE(range.Overlaps(range));
+
+  // Completely inside.
+  EXPECT_TRUE(range.Overlaps(AddressRange(102, 104)));
+
+  // Completely outside.
+  EXPECT_FALSE(range.Overlaps(AddressRange(1, 99)));
+  EXPECT_FALSE(range.Overlaps(AddressRange(200, 205)));
+
+  // Partially overlapping.
+  EXPECT_TRUE(range.Overlaps(AddressRange(0, 102)));
+  EXPECT_TRUE(range.Overlaps(AddressRange(102, 200)));
+}
+
+TEST(AddressRange, Union) {
+  constexpr AddressRange range(100, 105);
+  constexpr AddressRange empty;
+
+  // Union with itself.
+  EXPECT_EQ(range, range.Union(range));
+
+  // Union with empty. Shouldn't matter where the empty range is. Check both sides being empty.
+  EXPECT_EQ(range, range.Union(empty));
+  EXPECT_EQ(range, empty.Union(range));
+  EXPECT_EQ(range, range.Union(AddressRange(1000, 1000)));
+  EXPECT_EQ(range, AddressRange(1000, 1000).Union(range));
+
+  // Completely inside.
+  EXPECT_EQ(range, range.Union(AddressRange(102, 104)));
+
+  // Completely outside.
+  EXPECT_EQ(AddressRange(1, 105), range.Union(AddressRange(1, 99)));
+  EXPECT_EQ(AddressRange(100, 205), range.Union(AddressRange(200, 205)));
+
+  // Partially overlapping.
+  EXPECT_EQ(AddressRange(0, 105), range.Union(AddressRange(0, 102)));
+  EXPECT_EQ(AddressRange(100, 200), range.Union(AddressRange(102, 200)));
+}
+
+}  // namespace debug_ipc