Add 26.0 api compatibility check infrastructure.

Add support to the treble_sepolicy_tests suite that explicitly look at
the old and current policy versions, as well as the compatibility file,
to determine if any new types have been added without a compatibility
entry.  This first test catches the most common and likely changes that
could change the type label of an object for which vendor policy may have
needed access.  It also should prove the basis for additional compatibility
checks between old and new policies.

Bug: 36899958
Test: Policy builds and tests pass.
Change-Id: I609c913e6354eb10a04cc1a029ddd9fa0e592a4c
diff --git a/Android.mk b/Android.mk
index 8ec1ebf..0503b55 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1155,30 +1155,121 @@
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
-# 26.0_compat - the current plat_sepolicy.cil built
-# with the compatibility file targeting the 26.0
-# SELinux release.
+# 26.0_plat - the platform policy shipped as part of the 26.0 release.  This is
+# built to enable us to determine the diff between the current policy and the
+# 26.0 policy, which will be used in tests to make sure that compatibility has
+# been maintained by our mapping files.
+26.0_PLAT_PUBLIC_POLICY := $(LOCAL_PATH)/prebuilts/api/26.0/public
+26.0_PLAT_PRIVATE_POLICY := $(LOCAL_PATH)/prebuilts/api/26.0/private
+26.0_plat_policy.conf := $(intermediates)/26.0_plat_policy.conf
+$(26.0_plat_policy.conf): PRIVATE_MLS_SENS := $(MLS_SENS)
+$(26.0_plat_policy.conf): PRIVATE_MLS_CATS := $(MLS_CATS)
+$(26.0_plat_policy.conf): PRIVATE_TGT_ARCH := $(my_target_arch)
+$(26.0_plat_policy.conf): PRIVATE_TGT_WITH_ASAN := $(with_asan)
+$(26.0_plat_policy.conf): PRIVATE_ADDITIONAL_M4DEFS := $(LOCAL_ADDITIONAL_M4DEFS)
+$(26.0_plat_policy.conf): PRIVATE_FULL_TREBLE := true
+$(26.0_plat_policy.conf): $(call build_policy, $(sepolicy_build_files), \
+$(26.0_PLAT_PUBLIC_POLICY) $(26.0_PLAT_PRIVATE_POLICY))
+	$(transform-policy-to-conf)
+	$(hide) sed '/dontaudit/d' $@ > $@.dontaudit
+
+built_26.0_plat_sepolicy := $(intermediates)/built_26.0_plat_sepolicy
+$(built_26.0_plat_sepolicy): PRIVATE_ADDITIONAL_CIL_FILES := \
+  $(call build_policy, technical_debt.cil , $(26.0_PLAT_PRIVATE_POLICY))
+$(built_26.0_plat_sepolicy): $(26.0_plat_policy.conf) $(HOST_OUT_EXECUTABLES)/checkpolicy \
+  $(HOST_OUT_EXECUTABLES)/secilc \
+  $(call build_policy, technical_debt.cil, $(26.0_PLAT_PRIVATE_POLICY))
+	@mkdir -p $(dir $@)
+	$(hide) $(CHECKPOLICY_ASAN_OPTIONS) $(HOST_OUT_EXECUTABLES)/checkpolicy -M -C -c \
+		$(POLICYVERS) -o $@ $<
+	$(hide) cat $(PRIVATE_ADDITIONAL_CIL_FILES) >> $@
+	$(hide) $(HOST_OUT_EXECUTABLES)/secilc -M true -G -c $(POLICYVERS) $@ -o $@ -f /dev/null
+
+26.0_plat_policy.conf :=
+
+
+# 26.0_compat - the current plat_sepolicy.cil built with the compatibility file
+# targeting the 26.0 SELinux release.  This ensures that our policy will build
+# when used on a device that has non-platform policy targetting the 26.0 release.
 26.0_compat := $(intermediates)/26.0_compat
-26.0_mapping_cil := $(LOCAL_PATH)/prebuilts/api/26.0/26.0.cil
+26.0_mapping.cil := $(LOCAL_PATH)/prebuilts/api/26.0/26.0.cil
+26.0_mapping.ignore.cil := $(LOCAL_PATH)/prebuilts/api/26.0/26.0.ignore.cil
 26.0_nonplat := $(LOCAL_PATH)/prebuilts/api/26.0/nonplat_sepolicy.cil
 $(26.0_compat): PRIVATE_CIL_FILES := \
-$(built_plat_cil) $(26.0_mapping_cil) $(26.0_nonplat)
+$(built_plat_cil) $(26.0_mapping.cil) $(26.0_nonplat)
 $(26.0_compat): $(HOST_OUT_EXECUTABLES)/secilc \
-$(built_plat_cil) $(26.0_mapping_cil) $(26.0_nonplat)
+$(built_plat_cil) $(26.0_mapping.cil) $(26.0_nonplat)
 	$(hide) $(HOST_OUT_EXECUTABLES)/secilc -M true -G -N -c $(POLICYVERS) \
 		$(PRIVATE_CIL_FILES) -o $@ -f /dev/null
 
+# 26.0_mapping.combined.cil - a combination of the mapping file used when
+# combining the current platform policy with nonplatform policy based on the
+# 26.0 policy release and also a special ignored file that exists purely for
+# these tests.
+26.0_mapping.combined.cil := $(intermediates)/26.0_mapping.combined.cil
+$(26.0_mapping.combined.cil): $(26.0_mapping.cil) $(26.0_mapping.ignore.cil)
+	mkdir -p $(dir $@)
+	cat $^ > $@
+
+# plat_sepolicy - the current platform policy only, built into a policy binary.
+# TODO - this currently excludes partner extensions, but support should be added
+# to enable partners to add their own compatibility mapping
+BASE_PLAT_PUBLIC_POLICY := $(filter-out $(BOARD_PLAT_PUBLIC_SEPOLICY_DIR), $(PLAT_PUBLIC_POLICY))
+BASE_PLAT_PRIVATE_POLICY := $(filter-out $(BOARD_PLAT_PRIVATE_SEPOLICY_DIR), $(PLAT_PRIVATE_POLICY))
+base_plat_policy.conf := $(intermediates)/base_plat_policy.conf
+$(base_plat_policy.conf): PRIVATE_MLS_SENS := $(MLS_SENS)
+$(base_plat_policy.conf): PRIVATE_MLS_CATS := $(MLS_CATS)
+$(base_plat_policy.conf): PRIVATE_TGT_ARCH := $(my_target_arch)
+$(base_plat_policy.conf): PRIVATE_TGT_WITH_ASAN := $(with_asan)
+$(base_plat_policy.conf): PRIVATE_ADDITIONAL_M4DEFS := $(LOCAL_ADDITIONAL_M4DEFS)
+$(base_plat_policy.conf): PRIVATE_FULL_TREBLE := true
+$(base_plat_policy.conf): $(call build_policy, $(sepolicy_build_files), \
+$(BASE_PLAT_PUBLIC_POLICY) $(BASE_PLAT_PRIVATE_POLICY))
+	$(transform-policy-to-conf)
+	$(hide) sed '/dontaudit/d' $@ > $@.dontaudit
+
+built_plat_sepolicy := $(intermediates)/built_plat_sepolicy
+$(built_plat_sepolicy): PRIVATE_ADDITIONAL_CIL_FILES := \
+  $(call build_policy, $(sepolicy_build_cil_workaround_files), $(BASE_PLAT_PRIVATE_POLICY))
+$(built_plat_sepolicy): $(base_plat_policy.conf) $(HOST_OUT_EXECUTABLES)/checkpolicy \
+$(HOST_OUT_EXECUTABLES)/secilc \
+$(call build_policy, $(sepolicy_build_cil_workaround_files), $(BASE_PLAT_PRIVATE_POLICY))
+	@mkdir -p $(dir $@)
+	$(hide) $(CHECKPOLICY_ASAN_OPTIONS) $(HOST_OUT_EXECUTABLES)/checkpolicy -M -C -c \
+		$(POLICYVERS) -o $@ $<
+	$(hide) cat $(PRIVATE_ADDITIONAL_CIL_FILES) >> $@
+	$(hide) $(HOST_OUT_EXECUTABLES)/secilc -M true -G -c $(POLICYVERS) $@ -o $@ -f /dev/null
+
 treble_sepolicy_tests := $(intermediates)/treble_sepolicy_tests
 $(treble_sepolicy_tests): PRIVATE_PLAT_FC := $(built_plat_fc)
 $(treble_sepolicy_tests): PRIVATE_NONPLAT_FC := $(built_nonplat_fc)
 $(treble_sepolicy_tests): PRIVATE_SEPOLICY := $(built_sepolicy)
+$(treble_sepolicy_tests): PRIVATE_SEPOLICY_OLD := $(built_26.0_plat_sepolicy)
+$(treble_sepolicy_tests): PRIVATE_COMBINED_MAPPING := $(26.0_mapping.combined.cil)
+$(treble_sepolicy_tests): PRIVATE_PLAT_SEPOLICY := $(built_plat_sepolicy)
 $(treble_sepolicy_tests): $(HOST_OUT_EXECUTABLES)/treble_sepolicy_tests.py \
-$(built_plat_fc) $(built_nonplat_fc) $(built_sepolicy) $(26.0_compat)
+$(built_plat_fc) $(built_nonplat_fc) $(built_sepolicy) $(built_plat_sepolicy) \
+$(built_26.0_plat_sepolicy) $(26.0_compat) $(26.0_mapping.combined.cil)
 	@mkdir -p $(dir $@)
-	$(hide) python $(HOST_OUT_EXECUTABLES)/treble_sepolicy_tests.py -l $(HOST_OUT)/lib64 -f $(PRIVATE_PLAT_FC) -f $(PRIVATE_NONPLAT_FC) -p $(PRIVATE_SEPOLICY)
+	$(hide) python $(HOST_OUT_EXECUTABLES)/treble_sepolicy_tests.py -l \
+		$(HOST_OUT)/lib64 -f $(PRIVATE_PLAT_FC) -f $(PRIVATE_NONPLAT_FC) \
+		-b $(PRIVATE_PLAT_SEPOLICY) -m $(PRIVATE_COMBINED_MAPPING) \
+		-o $(PRIVATE_SEPOLICY_OLD) -p $(PRIVATE_SEPOLICY)
 	$(hide) touch $@
 
+26.0_PLAT_PUBLIC_POLICY :=
+26.0_PLAT_PRIVATE_POLICY :=
 26.0_compat :=
+26.0_mapping.cil :=
+26.0_mapping.combined.cil :=
+26.0_mapping.ignore.cil :=
+26.0_nonplat :=
+BASE_PLAT_PUBLIC_POLICY :=
+BASE_PLAT_PRIVATE_POLICY :=
+base_plat_policy.conf :=
+built_26.0_plat_sepolicy :=
+plat_sepolicy :=
+
 endif # ($(PRODUCT_FULL_TREBLE),true)
 #################################
 
diff --git a/tests/Android.bp b/tests/Android.bp
index e875497..19aca9c 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -7,6 +7,12 @@
 }
 
 cc_prebuilt_binary {
+    name: "mini_parser.py",
+    srcs: ["mini_parser.py"],
+    host_supported: true,
+}
+
+cc_prebuilt_binary {
     name: "policy.py",
     srcs: ["policy.py"],
     host_supported: true,
@@ -17,7 +23,7 @@
     name: "treble_sepolicy_tests.py",
     srcs: ["treble_sepolicy_tests.py"],
     host_supported: true,
-    required: ["policy.py"],
+    required: ["mini_parser.py", "policy.py"],
 }
 
 cc_prebuilt_binary {
diff --git a/tests/mini_parser.py b/tests/mini_parser.py
new file mode 100644
index 0000000..57b3d59
--- /dev/null
+++ b/tests/mini_parser.py
@@ -0,0 +1,100 @@
+from os.path import basename
+import re
+import sys
+
+# A very limited parser whose job is to process the compatibility mapping
+# files and retrieve type and attribute information until proper support is
+# built into libsepol
+
+# get the text in the next matching parens
+
+class MiniCilParser:
+    types = set() # types declared in mapping
+    pubtypes = set()
+    typeattributes = set() # attributes declared in mapping
+    typeattributesets = {} # sets defined in mapping
+    rTypeattributesets = {} # reverse mapping of above sets
+    apiLevel = None
+
+    def _getNextStmt(self, infile):
+        parens = 0
+        s = ""
+        c = infile.read(1)
+        # get to first statement
+        while c and c != "(":
+            c = infile.read(1)
+
+        parens += 1
+        c = infile.read(1)
+        while c and parens != 0:
+            s += c
+            c = infile.read(1)
+            if c == ';':
+                # comment, get rid of rest of the line
+                while c != '\n':
+                    c = infile.read(1)
+            elif c == '(':
+                parens += 1
+            elif c == ')':
+                parens -= 1
+        return s
+
+    def _parseType(self, stmt):
+        m = re.match(r"type\s+(.+)", stmt)
+        self.types.update(set(m.group(1)))
+        return
+
+    def _parseTypeattribute(self, stmt):
+        m = re.match(r"typeattribute\s+(.+)", stmt)
+        self.typeattributes.update(set(m.group(1)))
+        return
+
+    def _parseTypeattributeset(self, stmt):
+        m = re.match(r"typeattributeset\s+(.+?)\s+\((.+?)\)", stmt, flags = re.M |re.S)
+        ta = m.group(1)
+        # this isn't proper expression parsing, but will do for our
+        # current use
+        tas = m.group(2).split()
+
+        if self.typeattributesets.get(ta) is None:
+            self.typeattributesets[ta] = set()
+        self.typeattributesets[ta].update(set(tas))
+        for t in tas:
+            if self.rTypeattributesets.get(t) is None:
+                self.rTypeattributesets[t] = set()
+            self.rTypeattributesets[t].update(set(ta))
+
+        # check to see if this typeattributeset is a versioned public type
+        pub = re.match(r"(\w+)_\d+_\d+", ta)
+        if pub is not None:
+            self.pubtypes.update(set(pub.group(1)))
+        return
+
+    def _parseStmt(self, stmt):
+        if re.match(r"type\s+.+", stmt):
+            self._parseType(stmt)
+        elif re.match(r"typeattribute\s+.+", stmt):
+            self._parseTypeattribute(stmt)
+        elif re.match(r"typeattributeset\s+.+", stmt):
+            self._parseTypeattributeset(stmt)
+        else:
+            m = re.match(r"(\w+)\s+.+", stmt)
+            ret = "Warning: Unknown statement type (" + m.group(1) + ") in "
+            ret += "mapping file, perhaps consider adding support for it in "
+            ret += "system/sepolicy/tests/mini_parser.py!\n"
+            print ret
+        return
+
+    def __init__(self, policyFile):
+        with open(policyFile, 'r') as infile:
+            s = self._getNextStmt(infile)
+            while s:
+                self._parseStmt(s)
+                s = self._getNextStmt(infile)
+        fn = basename(policyFile)
+        m = re.match(r"(\d+\.\d+).+\.cil", fn)
+        self.apiLevel = m.group(1)
+
+if __name__ == '__main__':
+    f = sys.argv[1]
+    p = MiniCilParser(f)
diff --git a/tests/policy.py b/tests/policy.py
index b70b836..15a537e 100644
--- a/tests/policy.py
+++ b/tests/policy.py
@@ -123,6 +123,26 @@
                 continue
             yield Rule
 
+
+    def GetAllTypes(self):
+        TypeIterP = self.__libsepolwrap.init_type_iter(self.__policydbP, None, False)
+        if (TypeIterP == None):
+            sys.exit("Failed to initialize type iterator")
+        buf = create_string_buffer(self.__BUFSIZE)
+        AllTypes = set()
+        while True:
+            ret = self.__libsepolwrap.get_type(buf, self.__BUFSIZE,
+                    self.__policydbP, TypeIterP)
+            if ret == 0:
+                AllTypes.add(buf.value)
+                continue
+            if ret == 1:
+                break;
+            # We should never get here.
+            sys.exit("Failed to import policy")
+        self.__libsepolwrap.destroy_type_iter(TypeIterP)
+        return AllTypes
+
     def __GetTypesByFilePathPrefix(self, MatchPrefixes, DoNotMatchPrefixes):
         Types = set()
         for Type in self.__FcDict:
@@ -203,6 +223,8 @@
 
     # load file_contexts
     def __InitFC(self, FcPaths):
+        if FcPaths is None:
+            return
         fc = []
         for path in FcPaths:
             if not os.path.exists(path):
diff --git a/tests/sepol_wrap.cpp b/tests/sepol_wrap.cpp
index a12d438..cd53367 100644
--- a/tests/sepol_wrap.cpp
+++ b/tests/sepol_wrap.cpp
@@ -17,8 +17,11 @@
 #include <android-base/strings.h>
 #include <sepol_wrap.h>
 
-
+#define TYPE_ITER_LOOKUP   0
+#define TYPE_ITER_ALLTYPES 1
+#define TYPE_ITER_ALLATTRS 2
 struct type_iter {
+    unsigned int alltypes;
     type_datum *d;
     ebitmap_node *n;
     unsigned int length;
@@ -36,23 +39,33 @@
         return NULL;
     }
 
-    out->d = static_cast<type_datum *>(hashtab_search(db->p_types.table, type));
-    if (is_attr && out->d->flavor != TYPE_ATTRIB) {
-        std::cerr << "\"" << type << "\" MUST be an attribute in the policy" << std::endl;
-        free(out);
-        return NULL;
-    } else if (!is_attr && out->d->flavor !=TYPE_TYPE) {
-        std::cerr << "\"" << type << "\" MUST be a type in the policy" << std::endl;
-        free(out);
-        return NULL;
-    }
-
-    if (is_attr) {
-        out->bit = ebitmap_start(&db->attr_type_map[out->d->s.value - 1], &out->n);
-        out->length = ebitmap_length(&db->attr_type_map[out->d->s.value - 1]);
+    if (type == NULL) {
+        out->length = db->p_types.nprim;
+        out->bit = 0;
+        if (is_attr)
+            out->alltypes = TYPE_ITER_ALLATTRS;
+        else
+            out->alltypes = TYPE_ITER_ALLTYPES;
     } else {
-        out->bit = ebitmap_start(&db->type_attr_map[out->d->s.value - 1], &out->n);
-        out->length = ebitmap_length(&db->type_attr_map[out->d->s.value - 1]);
+        out->alltypes = TYPE_ITER_LOOKUP;
+        out->d = static_cast<type_datum *>(hashtab_search(db->p_types.table, type));
+        if (is_attr && out->d->flavor != TYPE_ATTRIB) {
+            std::cerr << "\"" << type << "\" MUST be an attribute in the policy" << std::endl;
+            free(out);
+            return NULL;
+        } else if (!is_attr && out->d->flavor !=TYPE_TYPE) {
+            std::cerr << "\"" << type << "\" MUST be a type in the policy" << std::endl;
+            free(out);
+            return NULL;
+        }
+
+        if (is_attr) {
+            out->bit = ebitmap_start(&db->attr_type_map[out->d->s.value - 1], &out->n);
+            out->length = ebitmap_length(&db->attr_type_map[out->d->s.value - 1]);
+        } else {
+            out->bit = ebitmap_start(&db->type_attr_map[out->d->s.value - 1], &out->n);
+            out->length = ebitmap_length(&db->type_attr_map[out->d->s.value - 1]);
+        }
     }
 
     return static_cast<void *>(out);
@@ -65,7 +78,7 @@
 }
 
 /*
- * print allow rule into *out buffer.
+ * print type into *out buffer.
  *
  * Returns -1 on error.
  * Returns 0 on successfully reading an avtab entry.
@@ -77,20 +90,28 @@
     policydb_t *db = static_cast<policydb_t *>(policydbp);
     struct type_iter *i = static_cast<struct type_iter *>(type_iterp);
 
-    for (; i->bit < i->length; i->bit = ebitmap_next(&i->n, i->bit)) {
-        if (!ebitmap_node_get_bit(i->n, i->bit)) {
-            continue;
+    if (!i->alltypes) {
+        for (; i->bit < i->length; i->bit = ebitmap_next(&i->n, i->bit)) {
+            if (!ebitmap_node_get_bit(i->n, i->bit)) {
+                continue;
+            }
+            break;
         }
-        len = snprintf(out, max_size, "%s", db->p_type_val_to_name[i->bit]);
-        if (len >= max_size) {
-               std::cerr << "type name exceeds buffer size." << std::endl;
-               return -1;
-        }
-        i->bit = ebitmap_next(&i->n, i->bit);
-        return 0;
     }
-
-    return 1;
+    if (i->bit >= i->length)
+        return 1;
+    while ((i->alltypes == TYPE_ITER_ALLATTRS
+            && db->type_val_to_struct[i->bit]->flavor != TYPE_ATTRIB)
+            || (i->alltypes == TYPE_ITER_ALLTYPES
+            && db->type_val_to_struct[i->bit]->flavor != TYPE_TYPE))
+        i->bit++;
+    len = snprintf(out, max_size, "%s", db->p_type_val_to_name[i->bit]);
+    if (len >= max_size) {
+        std::cerr << "type name exceeds buffer size." << std::endl;
+        return -1;
+    }
+    i->alltypes ? i->bit++ : i->bit = ebitmap_next(&i->n, i->bit);
+    return 0;
 }
 
 void *load_policy(const char *policy_path)
diff --git a/tests/treble_sepolicy_tests.py b/tests/treble_sepolicy_tests.py
index b358a14..7584cab 100644
--- a/tests/treble_sepolicy_tests.py
+++ b/tests/treble_sepolicy_tests.py
@@ -1,6 +1,7 @@
 from optparse import OptionParser
 from optparse import Option, OptionValueError
 import os
+import mini_parser
 import policy
 from policy import MatchPathPrefix
 import re
@@ -70,6 +71,11 @@
 appdomains = set()
 vendordomains = set()
 
+# compat vars
+alltypes = set()
+oldalltypes = set()
+compatMapping = None
+
 def GetAllDomains(pol):
     global alldomains
     for result in pol.QueryTypeAttribute("domain", True):
@@ -87,7 +93,6 @@
             alldomains[d].appdomain = True
             appdomains.add(d)
 
-
 def GetCoreDomains():
     global alldomains
     global coredomains
@@ -147,6 +152,12 @@
         for result in pol.QueryTypeAttribute(domain, False):
             alldomains[domain].attributes.add(result)
 
+def GetAllTypes(pol, oldpol):
+    global alltypes
+    global oldalltypes
+    alltypes = pol.GetAllTypes()
+    oldalltypes = oldpol.GetAllTypes()
+
 def setup(pol):
     GetAllDomains(pol)
     GetAttributes(pol)
@@ -154,6 +165,13 @@
     GetAppDomains()
     GetCoreDomains()
 
+# setup for the policy compatibility tests
+def compatSetup(pol, oldpol, mapping):
+    global compatMapping
+
+    GetAllTypes(pol, oldpol)
+    compatMapping = mapping
+
 #############################################################
 # Tests
 #############################################################
@@ -189,6 +207,30 @@
     return ret
 
 ###
+# Make sure that any new type introduced in the new policy that was not present
+# in the old policy has been recorded in the mapping file.
+def TestNoUnmappedNewTypes():
+    global alltypes
+    global oldalltypes
+    newt = alltypes - oldalltypes
+    ret = ""
+    violators = []
+
+    for n in newt:
+        if compatMapping.rTypeattributesets.get(n) is None:
+            violators.append(n)
+
+    if len(violators) > 0:
+        ret += "SELinux: The following types were found added to the policy "
+        ret += "without an entry into the compatibility mapping file(s) found "
+        ret += "in prebuilts/api/" + compatMapping.apiLevel + "/\n"
+        ret += " ".join(str(x) for x in sorted(violators)) + "\n"
+    return ret
+
+def TestTrebleCompatMapping():
+    ret = TestNoUnmappedNewTypes()
+    return ret
+###
 # extend OptionParser to allow the same option flag to be used multiple times.
 # This is used to allow multiple file_contexts files and tests to be
 # specified.
@@ -205,17 +247,23 @@
         else:
             Option.take_action(self, action, dest, opt, value, values, parser)
 
-Tests = ["CoredomainViolations"]
+Tests = {"CoredomainViolations": TestCoredomainViolations,
+         "TrebleCompatMapping": TestTrebleCompatMapping }
 
 if __name__ == '__main__':
     usage = "treble_sepolicy_tests.py -f nonplat_file_contexts -f "
-    usage +="plat_file_contexts -p policy [--test test] [--help]"
+    usage +="plat_file_contexts -p curr_policy -b base_policy -o old_policy "
+    usage +="-m mapping file [--test test] [--help]"
     parser = OptionParser(option_class=MultipleOption, usage=usage)
+    parser.add_option("-b", "--basepolicy", dest="basepolicy", metavar="FILE")
     parser.add_option("-f", "--file_contexts", dest="file_contexts",
             metavar="FILE", action="extend", type="string")
-    parser.add_option("-p", "--policy", dest="policy", metavar="FILE")
     parser.add_option("-l", "--library-path", dest="libpath", metavar="FILE")
+    parser.add_option("-m", "--mapping", dest="mapping", metavar="FILE")
+    parser.add_option("-o", "--oldpolicy", dest="oldpolicy", metavar="FILE")
+    parser.add_option("-p", "--policy", dest="policy", metavar="FILE")
     parser.add_option("-t", "--test", dest="tests", action="extend",
+
             help="Test options include "+str(Tests))
 
     (options, args) = parser.parse_args()
@@ -225,9 +273,14 @@
     if not os.path.exists(options.libpath):
         sys.exit("Error: library-path " + options.libpath + " does not exist\n"
                 + parser.usage)
-
+    if not options.basepolicy:
+        sys.exit("Must specify the current platform-only policy file\n" + parser.usage)
+    if not options.mapping:
+        sys.exit("Must specify a compatibility mapping file\n" + parser.usage)
+    if not options.oldpolicy:
+        sys.exit("Must specify the previous monolithic policy file\n" + parser.usage)
     if not options.policy:
-        sys.exit("Must specify monolithic policy file\n" + parser.usage)
+        sys.exit("Must specify current monolithic policy file\n" + parser.usage)
     if not os.path.exists(options.policy):
         sys.exit("Error: policy file " + options.policy + " does not exist\n"
                 + parser.usage)
@@ -241,17 +294,30 @@
 
     pol = policy.Policy(options.policy, options.file_contexts, options.libpath)
     setup(pol)
+    basepol = policy.Policy(options.basepolicy, None, options.libpath)
+    oldpol = policy.Policy(options.oldpolicy, None, options.libpath)
+    mapping = mini_parser.MiniCilParser(options.mapping)
+    compatSetup(basepol, oldpol, mapping)
 
     if DEBUG:
         PrintScontexts()
 
     results = ""
     # If an individual test is not specified, run all tests.
-    if ( options.tests is None
-        or ("CoredomainViolations" in options.tests and len(options.tests) == 1)):
-        results += TestCoredomainViolations()
+    if options.tests is None:
+        for t in Tests.values():
+            results += t()
     else:
-        sys.exit("Error: unknown test(s): " + str(options.tests))
+        for tn in options.tests:
+            t = Tests.get(tn)
+            if t:
+                results += t()
+            else:
+                err = "Error: unknown test: " + tn + "\n"
+                err += "Available tests:\n"
+                for tn in Tests.keys():
+                    err += tn + "\n"
+                sys.exit(err)
 
     if len(results) > 0:
         sys.exit(results)