diff --git a/extensions/pom.xml b/extensions/pom.xml
new file mode 100644
index 000000000..64c872308
--- /dev/null
+++ b/extensions/pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+
+ com.google.truth
+ truth-parent
+ 1.0-SNAPSHOT
+
+ com.google.truth.extensions
+ truth-extensions-parent
+ pom
+ Truth Extensions (Parent)
+
+ Parent metdata for a collection of Truth extensions, Subjects, utilities for
+ the Truth assertion framework.
+
+
+ re2j
+
+
diff --git a/extensions/re2j/pom.xml b/extensions/re2j/pom.xml
new file mode 100644
index 000000000..34582a776
--- /dev/null
+++ b/extensions/re2j/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+ com.google.truth.extensions
+ truth-extensions-parent
+ 1.0-SNAPSHOT
+
+ truth-re2j-extension
+ Truth Extension for RE2J
+
+ An extension for the Truth test assertion framework supporting RE2J patterns
+
+
+
+ com.google.truth
+ truth
+ 1.0-SNAPSHOT
+
+
+ com.google.re2j
+ re2j
+ 1.0
+
+
+
+
+
diff --git a/extensions/re2j/src/main/java/com/google/common/truth/extensions/re2j/Re2jSubjects.java b/extensions/re2j/src/main/java/com/google/common/truth/extensions/re2j/Re2jSubjects.java
new file mode 100644
index 000000000..bdee8d796
--- /dev/null
+++ b/extensions/re2j/src/main/java/com/google/common/truth/extensions/re2j/Re2jSubjects.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * 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.
+ */
+package com.google.common.truth.extensions.re2j;
+
+import com.google.common.annotations.GwtIncompatible;
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+import com.google.re2j.Pattern;
+
+/**
+ * Truth subjects for re2j regular expressions.
+ *
+ *
Truth natively provides subjects for dealing with {@code java.util.regex} based regular
+ * expressions. This class is intended to provide {@code com.google.re2j} analogues to those
+ * methods.
+ */
+public final class Re2jSubjects {
+ /**
+ * Returns a subject factory for {@link String} subjects which you can use to assert things about
+ * {@link com.google.re2j.Pattern} regexes.
+ *
+ *
This subject does not replace Truth's built-in {@link com.google.common.truth.StringSubject}
+ * but instead provides only the methods needed to deal with regular expressions.
+ *
+ * @see com.google.common.truth.StringSubject
+ */
+ public static SubjectFactory re2jString() {
+ return Re2jStringSubject.FACTORY;
+ }
+
+ /**
+ * Subject for {@link String} subjects which you can use to assert things about
+ * {@link com.google.re2j.Pattern} regexes.
+ *
+ * @see #re2jString
+ */
+ public static final class Re2jStringSubject extends Subject {
+ private static final SubjectFactory FACTORY =
+ new SubjectFactory() {
+ @Override
+ public Re2jStringSubject getSubject(FailureStrategy fs, String target) {
+ return new Re2jStringSubject(fs, target);
+ }
+ };
+
+ private Re2jStringSubject(FailureStrategy failureStrategy, String subject) {
+ super(failureStrategy, subject);
+ }
+
+ @Override
+ protected String getDisplaySubject() {
+ if (internalCustomName() != null) {
+ return internalCustomName() + " (<" + quote(getSubject()) + ">)";
+ } else {
+ return "<" + quote(getSubject()) + ">";
+ }
+ }
+
+ /**
+ * Fails if the string does not match the given regex.
+ */
+ public void matches(String regex) {
+ if (!Pattern.matches(regex, getSubject())) {
+ fail("matches", regex);
+ }
+ }
+
+ /**
+ * Fails if the string does not match the given regex.
+ */
+ @GwtIncompatible("com.google.re2j.Pattern")
+ public void matches(Pattern regex) {
+ if (!regex.matcher(getSubject()).matches()) {
+ fail("matches", regex);
+ }
+ }
+
+ /**
+ * Fails if the string matches the given regex.
+ */
+ public void doesNotMatch(String regex) {
+ if (Pattern.matches(regex, getSubject())) {
+ fail("fails to match", regex);
+ }
+ }
+
+ /**
+ * Fails if the string matches the given regex.
+ */
+ @GwtIncompatible("com.google.re2j.Pattern")
+ public void doesNotMatch(Pattern regex) {
+ if (regex.matcher(getSubject()).matches()) {
+ fail("fails to match", regex);
+ }
+ }
+
+ /**
+ * Fails if the string does not contain a match on the given regex.
+ */
+ @GwtIncompatible("com.google.re2j.Pattern")
+ public void containsMatch(Pattern pattern) {
+ if (!pattern.matcher(getSubject()).find()) {
+ failWithRawMessage(
+ "%s should have contained a match for <%s>",
+ getDisplaySubject(),
+ pattern);
+ }
+ }
+
+ /**
+ * Fails if the string does not contain a match on the given regex.
+ */
+ public void containsMatch(String regex) {
+ if (!containsMatch(getSubject(), regex)) {
+ failWithRawMessage("%s should have contained a match for <%s>", getDisplaySubject(), regex);
+ }
+ }
+
+ /**
+ * Fails if the string contains a match on the given regex.
+ */
+ @GwtIncompatible("com.google.re2j.Pattern")
+ public void doesNotContainMatch(Pattern pattern) {
+ if (pattern.matcher(getSubject()).find()) {
+ failWithRawMessage("%s should not have contained a match for <%s>",
+ getDisplaySubject(), pattern);
+ }
+ }
+
+ /**
+ * Fails if the string contains a match on the given regex.
+ */
+ public void doesNotContainMatch(String regex) {
+ if (containsMatch(getSubject(), regex)) {
+ failWithRawMessage("%s should not have contained a match for <%s>",
+ getDisplaySubject(), regex);
+ }
+ }
+
+ private static String quote(CharSequence toBeWrapped) {
+ return "\"" + toBeWrapped + "\"";
+ }
+
+ private static boolean containsMatch(String subject, String regex) {
+ return Pattern.compile(regex).matcher(subject).find();
+ }
+ }
+
+ private Re2jSubjects() {}
+}
diff --git a/extensions/re2j/src/test/java/com/google/common/truth/extensions/re2j/Re2jSubjectsTest.java b/extensions/re2j/src/test/java/com/google/common/truth/extensions/re2j/Re2jSubjectsTest.java
new file mode 100644
index 000000000..9b19a2d9c
--- /dev/null
+++ b/extensions/re2j/src/test/java/com/google/common/truth/extensions/re2j/Re2jSubjectsTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * 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.
+ */
+package com.google.common.truth.extensions.re2j;
+
+import static com.google.common.truth.extensions.re2j.Re2jSubjects.re2jString;
+import static com.google.common.truth.Truth.assertAbout;
+
+import com.google.re2j.Pattern;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link Re2jSubjects}. */
+@RunWith(JUnit4.class)
+public class Re2jSubjectsTest {
+ private static final String PATTERN_STR = "(?:hello )+world";
+ private static final Pattern PATTERN = Pattern.compile(PATTERN_STR);
+
+ @Test
+ public void matches_string_succeeds() {
+ assertAbout(re2jString()).that("hello world").matches(PATTERN_STR);
+ }
+
+ @Test
+ public void matches_pattern_succeeds() {
+ assertAbout(re2jString()).that("hello world").matches(PATTERN);
+ }
+
+ @Test
+ public void doesNotMatch_string_succeeds() {
+ assertAbout(re2jString()).that("world").doesNotMatch(PATTERN_STR);
+ }
+
+ @Test
+ public void doesNotMatch_pattern_succeeds() {
+ assertAbout(re2jString()).that("world").doesNotMatch(PATTERN);
+ }
+
+ @Test
+ public void containsMatch_string_succeeds() {
+ assertAbout(re2jString()).that("this is a hello world").containsMatch(PATTERN_STR);
+ }
+
+ @Test
+ public void containsMatch_pattern_succeeds() {
+ assertAbout(re2jString()).that("this is a hello world").containsMatch(PATTERN);
+ }
+
+ @Test
+ public void doesNotContainMatch_string_succeeds() {
+ assertAbout(re2jString()).that("hello cruel world").doesNotContainMatch(PATTERN_STR);
+ }
+
+ @Test
+ public void doesNotContainMatch_pattern_succeeds() {
+ assertAbout(re2jString()).that("hello cruel world").doesNotContainMatch(PATTERN);
+ }
+}
diff --git a/pom.xml b/pom.xml
index 0de255118..e444d7873 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,6 +17,7 @@
core
+ extensions
3.0.3