return out << "AABB(" << box.min << ", " << box.max << ')';
 }
 
+bool Intersection(const AABB &a, const AABB &b) noexcept {
+       if (a.max.x < b.min.x) return false;
+       if (b.max.x < a.min.x) return false;
+       if (a.max.y < b.min.y) return false;
+       if (b.max.y < a.min.y) return false;
+       if (a.max.z < b.min.z) return false;
+       if (b.max.z < a.min.z) return false;
+       return true;
+}
+
 std::ostream &operator <<(std::ostream &out, const Ray &ray) {
        return out << "Ray(" << ray.orig << ", " << ray.dir << ')';
 }
 
                glm::vec3 high(glm::max(glm::abs(min), glm::abs(max)));
                return glm::length(high);
        }
+
+       void Move(const glm::vec3 &delta) noexcept {
+               min += delta;
+               max += delta;
+       }
 };
 
 std::ostream &operator <<(std::ostream &, const AABB &);
 
+bool Intersection(const AABB &, const AABB &) noexcept;
+
 // TODO: this should really use setters/getters for dir and inv_dir so
 //       manipulating code doesn't "forget" to call Update()
 struct Ray {
 
 }
 
 
+void IntersectionTest::testAABB() {
+       AABB a{ { -1, -1, -1 }, { 1, 1, 1 } };
+       AABB b(a);
+
+       CPPUNIT_ASSERT_MESSAGE(
+               "coincidental AABBs should intersect",
+               Intersection(a, b)
+       );
+
+       b.Move({ 1, 0, 0 });
+       CPPUNIT_ASSERT_MESSAGE(
+               "AABBs should intersect",
+               Intersection(a, b)
+       );
+
+       b.Move({ 2, 0, 0 });
+       CPPUNIT_ASSERT_MESSAGE(
+               "AABBs should not intersect",
+               !Intersection(a, b)
+       );
+
+       b.Move({ -4, 0, 0 });
+       CPPUNIT_ASSERT_MESSAGE(
+               "AABBs should intersect",
+               Intersection(a, b)
+       );
+
+       b.Move({ -2, 0, 0 });
+       CPPUNIT_ASSERT_MESSAGE(
+               "AABBs should not intersect",
+               !Intersection(a, b)
+       );
+
+       b.Move({ 3, 1, 0 });
+       CPPUNIT_ASSERT_MESSAGE(
+               "AABBs should intersect",
+               Intersection(a, b)
+       );
+
+       b.Move({ 0, 2, 0 });
+       CPPUNIT_ASSERT_MESSAGE(
+               "AABBs should not intersect",
+               !Intersection(a, b)
+       );
+
+       b.Move({ 2, 0, 0 });
+       CPPUNIT_ASSERT_MESSAGE(
+               "AABBs should not intersect",
+               !Intersection(a, b)
+       );
+
+       b.Move({ 0, 0, 2 });
+       CPPUNIT_ASSERT_MESSAGE(
+               "AABBs should not intersect",
+               !Intersection(a, b)
+       );
+}
+
 void IntersectionTest::testSimpleRayBox() {
        Ray ray{ { 0, 0, 0 }, { 1, 0, 0 }, { } }; // at origin, pointing right
        ray.Update();