Merge "Revert "Refactor neverallow tests to be parameterized""
diff --git a/hostsidetests/security/Android.bp b/hostsidetests/security/Android.bp
index b30c9f6..4798f0c 100644
--- a/hostsidetests/security/Android.bp
+++ b/hostsidetests/security/Android.bp
@@ -23,6 +23,7 @@
     defaults: ["cts_defaults"],
     srcs: [
         "src/**/*.java",
+        ":CtsSecurityHostTestCases_LocalGeneratedSources",
     ],
     // tag this module as a cts test artifact
     test_suites: [
@@ -33,7 +34,6 @@
         "cts-tradefed",
         "tradefed",
         "compatibility-host-util",
-        "cts-host-utils",
     ],
     java_resources: [
         ":plat_seapp_contexts",
@@ -60,6 +60,19 @@
     target_required: ["CtsDeviceInfo"],
 }
 
+genrule {
+    name: "CtsSecurityHostTestCases_LocalGeneratedSources",
+    tools: [
+        "SELinuxNeverallowTestGen",
+        "soong_zip",
+    ],
+    srcs: [":general_sepolicy.conf"],
+    out: ["SELinuxNeverallowRulesTest.srcjar"],
+    cmd: "mkdir -p $(genDir)/android/cts/security && " +
+        "$(location SELinuxNeverallowTestGen) $(in) $(genDir)/android/cts/security/SELinuxNeverallowRulesTest.java && " +
+        "$(location soong_zip) -jar -o $(out) -C $(genDir) -D $(genDir)",
+}
+
 java_genrule_host {
     name: "CtsSecurityHostTestCases_StaticLibs",
     tools: [
@@ -77,7 +90,6 @@
     tool_files: [
         ":apex_sepolicy-33.cil",
         ":apex_sepolicy-33.decompiled.cil",
-        ":general_sepolicy.conf",
     ],
     out: ["CtsSecurityHostTestCases_StaticLibs.jar"],
     cmd: "$(location soong_zip) -jar -o $(location CtsSecurityHostTestCases_StaticLibs.jar) -j " +
@@ -91,6 +103,5 @@
         "-f $(location sepolicy_tests) " +
         "-f $(location treble_sepolicy_tests) " +
         "-f $(location :apex_sepolicy-33.cil) " +
-        "-f $(location :apex_sepolicy-33.decompiled.cil) " +
-        "-f $(location :general_sepolicy.conf)",
+        "-f $(location :apex_sepolicy-33.decompiled.cil)",
 }
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxNeverallowRulesTest.java b/hostsidetests/security/src/android/security/cts/SELinuxNeverallowRulesTest.java
deleted file mode 100644
index 3946185..0000000
--- a/hostsidetests/security/src/android/security/cts/SELinuxNeverallowRulesTest.java
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * 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 android.security.cts;
-
-import static org.junit.Assert.assertTrue;
-
-import android.cts.host.utils.DeviceJUnit4ClassRunnerWithParameters;
-import android.cts.host.utils.DeviceJUnit4Parameterized;
-import android.platform.test.annotations.RestrictedBuildTest;
-
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.util.PropertyUtil;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.InputStreamReader;
-import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- * Neverallow Rules SELinux tests.
- *
- * This is a parametrised test. It extracts the neverallow rules from the
- * platform policy which is embedded in the CTS distribution. Each rule
- * generates its own test to ensure that it is not violated by the device
- * policy.
- *
- * A set of criteria can be used in the platform policy to skip the test
- * depending on the device (e.g., launching version). See sConditions below.
- *
- */
-@RunWith(DeviceJUnit4Parameterized.class)
-@UseParametersRunnerFactory(DeviceJUnit4ClassRunnerWithParameters.RunnerFactory.class)
-public class SELinuxNeverallowRulesTest extends BaseHostJUnit4Test {
-    private File sepolicyAnalyze;
-    private File devicePolicyFile;
-    private File deviceSystemPolicyFile;
-
-    private IBuildInfo mBuild;
-    private int mVendorSepolicyVersion = -1;
-    private int mSystemSepolicyVersion = -1;
-
-    /**
-     * A reference to the device under test.
-     */
-    private ITestDevice mDevice;
-
-    private static String[] sConditions = {
-        "TREBLE_ONLY",
-        "COMPATIBLE_PROPERTY_ONLY",
-        "LAUNCHING_WITH_R_ONLY",
-        "LAUNCHING_WITH_S_ONLY",
-    };
-
-    private static class NeverAllowRule {
-        public String mText;
-        public boolean fullTrebleOnly;
-        public boolean launchingWithROnly;
-        public boolean launchingWithSOnly;
-        public boolean compatiblePropertyOnly;
-
-        NeverAllowRule(String text, HashMap<String, Integer> conditions) {
-            mText = text;
-            if (conditions.getOrDefault("TREBLE_ONLY", 0) > 0) {
-                fullTrebleOnly = true;
-            }
-            if (conditions.getOrDefault("COMPATIBLE_PROPERTY_ONLY", 0) > 0) {
-                compatiblePropertyOnly = true;
-            }
-            if (conditions.getOrDefault("LAUNCHING_WITH_R_ONLY", 0) > 0) {
-                launchingWithROnly = true;
-            }
-            if (conditions.getOrDefault("LAUNCHING_WITH_S_ONLY", 0) > 0) {
-                launchingWithSOnly = true;
-            }
-        }
-
-        public String toString() {
-            return "Rule [text= " + mText
-                   + ", fullTrebleOnly=" + fullTrebleOnly
-                   + ", compatiblePropertyOnly=" + compatiblePropertyOnly
-                   + ", launchingWithROnly=" + launchingWithROnly
-                   + ", launchingWithSOnly=" + launchingWithSOnly
-                   + "]";
-        }
-    }
-
-    /**
-     * Generate the test parameters based on the embedded policy (general_sepolicy.conf).
-     */
-    @Parameters
-    public static Iterable<NeverAllowRule> generateRules() throws Exception {
-        File publicPolicy = SELinuxHostTest.copyResourceToTempFile("/general_sepolicy.conf");
-        String policy = Files.readString(publicPolicy.toPath());
-
-        String patternConditions = Arrays.stream(sConditions)
-                .flatMap(condition -> Stream.of("BEGIN_" + condition, "END_" + condition))
-                .collect(Collectors.joining("|"));
-
-        /* Uncomment conditions delimiter lines. */
-        Pattern uncommentConditions = Pattern.compile("^\\s*#\\s*(" + patternConditions + ")\\s*$",
-                Pattern.MULTILINE);
-        Matcher matcher = uncommentConditions.matcher(policy);
-        policy = matcher.replaceAll("$1");
-
-        /* Remove all comments. */
-        Pattern comments = Pattern.compile("#.*?$", Pattern.MULTILINE);
-        matcher = comments.matcher(policy);
-        policy = matcher.replaceAll("");
-
-        /* Use a pattern to match all the neverallow rules or a condition. */
-        Pattern neverAllowPattern = Pattern.compile(
-                "^\\s*(neverallow\\s.+?;|" + patternConditions + ")",
-                Pattern.MULTILINE | Pattern.DOTALL);
-
-        ArrayList<NeverAllowRule> rules = new ArrayList();
-        HashMap<String, Integer> conditions = new HashMap();
-
-        matcher = neverAllowPattern.matcher(policy);
-        while (matcher.find()) {
-            String rule = matcher.group(1).replace("\n", " ");
-            if (rule.startsWith("BEGIN_")) {
-                String section = rule.substring(6);
-                conditions.put(section, conditions.getOrDefault(section, 0) + 1);
-            } else if (rule.startsWith("END_")) {
-                String section = rule.substring(4);
-                Integer v = conditions.getOrDefault(section, 0);
-                assertTrue("Condition " + rule + " found without BEGIN", v > 0);
-                conditions.put(section, v - 1);
-            } else if (rule.startsWith("neverallow")) {
-                rules.add(new NeverAllowRule(rule, conditions));
-            } else {
-                throw new Exception("Unknown rule: " + rule);
-            }
-        }
-
-        for (Map.Entry<String, Integer> condition : conditions.entrySet()) {
-            assertTrue("End of input while inside " + condition.getKey() + " section",
-                    condition.getValue() == 0);
-        }
-
-        assertTrue("No test generated from the CTS-embedded policy", !rules.isEmpty());
-        return rules;
-    }
-
-    /* Parameter generated by generateRules() and available to testNeverallowRules */
-    @Parameter
-    public NeverAllowRule mRule;
-
-    @Before
-    public void setUp() throws Exception {
-        mDevice = getDevice();
-        mBuild = getBuild();
-
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
-        sepolicyAnalyze = SELinuxHostTest.copyResourceToTempFile("/sepolicy-analyze");
-        sepolicyAnalyze.setExecutable(true);
-
-        devicePolicyFile = SELinuxHostTest.getDevicePolicyFile(mDevice);
-
-        if (isSepolicySplit()) {
-            deviceSystemPolicyFile =
-                    SELinuxHostTest.getDeviceSystemPolicyFile(mDevice);
-
-            // Caching this variable to save time.
-            if (mVendorSepolicyVersion == -1) {
-                mVendorSepolicyVersion =
-                        SELinuxHostTest.getVendorSepolicyVersion(mBuild, mDevice);
-            }
-            if (mSystemSepolicyVersion == -1) {
-                mSystemSepolicyVersion =
-                        SELinuxHostTest.getSystemSepolicyVersion(mBuild);
-            }
-        }
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        sepolicyAnalyze.delete();
-    }
-
-    private boolean isFullTrebleDevice() throws Exception {
-        return SELinuxHostTest.isFullTrebleDevice(mDevice);
-    }
-
-    private boolean isDeviceLaunchingWithR() throws Exception {
-        return PropertyUtil.getFirstApiLevel(mDevice) > 29;
-    }
-
-    private boolean isDeviceLaunchingWithS() throws Exception {
-        return PropertyUtil.getFirstApiLevel(mDevice) > 30;
-    }
-
-    private boolean isCompatiblePropertyEnforcedDevice() throws Exception {
-        return SELinuxHostTest.isCompatiblePropertyEnforcedDevice(mDevice);
-    }
-
-    private boolean isSepolicySplit() throws Exception {
-        return SELinuxHostTest.isSepolicySplit(mDevice);
-    }
-
-    @Test
-    @RestrictedBuildTest
-    public void testNeverallowRules() throws Exception {
-
-        if ((mRule.fullTrebleOnly) && (!isFullTrebleDevice())) {
-            // This test applies only to Treble devices but this device isn't one
-            return;
-        }
-        if ((mRule.launchingWithROnly) && (!isDeviceLaunchingWithR())) {
-            // This test applies only to devices launching with R or later but this device isn't one
-            return;
-        }
-        if ((mRule.launchingWithSOnly) && (!isDeviceLaunchingWithS())) {
-            // This test applies only to devices launching with S or later but this device isn't one
-            return;
-        }
-        if ((mRule.compatiblePropertyOnly) && (!isCompatiblePropertyEnforcedDevice())) {
-            // This test applies only to devices on which compatible property is enforced but this
-            // device isn't one
-            return;
-        }
-
-        // If sepolicy is split and vendor sepolicy version is behind platform's,
-        // only test against platform policy.
-        File policyFile =
-                (isSepolicySplit() && mVendorSepolicyVersion < mSystemSepolicyVersion)
-                ? deviceSystemPolicyFile : devicePolicyFile;
-
-        /* run sepolicy-analyze neverallow check on policy file using given neverallow rules */
-        ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
-                policyFile.getAbsolutePath(), "neverallow", "-n",
-                mRule.mText);
-        pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
-        pb.redirectErrorStream(true);
-        Process p = pb.start();
-        BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
-        String line;
-        StringBuilder errorString = new StringBuilder();
-        while ((line = result.readLine()) != null) {
-            errorString.append(line);
-            errorString.append("\n");
-        }
-        p.waitFor();
-        assertTrue("The following errors were encountered when validating the SELinux"
-                   + "neverallow rule:\n" + mRule.mText + "\n" + errorString,
-                   errorString.length() == 0);
-    }
-}
diff --git a/tools/selinux/Android.bp b/tools/selinux/Android.bp
index c3bee29..1339557 100644
--- a/tools/selinux/Android.bp
+++ b/tools/selinux/Android.bp
@@ -17,6 +17,17 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+python_library_host {
+    name: "SELinuxNeverallowTestFrame",
+    srcs: ["SELinuxNeverallowTestFrame.py"],
+}
+
+python_binary_host {
+    name: "SELinuxNeverallowTestGen",
+    srcs: ["SELinuxNeverallowTestGen.py"],
+    libs: ["SELinuxNeverallowTestFrame"],
+}
+
 sh_test_host {
     name: "seamendc-test",
     src: "seamendc-test.sh",
diff --git a/tools/selinux/SELinuxNeverallowTestFrame.py b/tools/selinux/SELinuxNeverallowTestFrame.py
new file mode 100644
index 0000000..ad7557a
--- /dev/null
+++ b/tools/selinux/SELinuxNeverallowTestFrame.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env python
+
+src_header = """/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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 android.security.cts;
+
+import android.platform.test.annotations.RestrictedBuildTest;
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.util.PropertyUtil;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IDeviceTest;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+/**
+ * Neverallow Rules SELinux tests.
+ */
+public class SELinuxNeverallowRulesTest extends DeviceTestCase implements IBuildReceiver, IDeviceTest {
+    private File sepolicyAnalyze;
+    private File devicePolicyFile;
+    private File deviceSystemPolicyFile;
+
+    private IBuildInfo mBuild;
+    private int mVendorSepolicyVersion = -1;
+    private int mSystemSepolicyVersion = -1;
+
+    /**
+     * A reference to the device under test.
+     */
+    private ITestDevice mDevice;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setBuild(IBuildInfo build) {
+        mBuild = build;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setDevice(ITestDevice device) {
+        super.setDevice(device);
+        mDevice = device;
+    }
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
+        sepolicyAnalyze = android.security.cts.SELinuxHostTest.copyResourceToTempFile("/sepolicy-analyze");
+        sepolicyAnalyze.setExecutable(true);
+
+        devicePolicyFile = android.security.cts.SELinuxHostTest.getDevicePolicyFile(mDevice);
+
+        if (isSepolicySplit()) {
+            deviceSystemPolicyFile =
+                    android.security.cts.SELinuxHostTest.getDeviceSystemPolicyFile(mDevice);
+
+            // Caching this variable to save time.
+            if (mVendorSepolicyVersion == -1) {
+                mVendorSepolicyVersion =
+                        android.security.cts.SELinuxHostTest.getVendorSepolicyVersion(mBuild, mDevice);
+            }
+            if (mSystemSepolicyVersion == -1) {
+                mSystemSepolicyVersion =
+                        android.security.cts.SELinuxHostTest.getSystemSepolicyVersion(mBuild);
+            }
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        sepolicyAnalyze.delete();
+    }
+
+    private boolean isFullTrebleDevice() throws Exception {
+        return android.security.cts.SELinuxHostTest.isFullTrebleDevice(mDevice);
+    }
+
+    private boolean isDeviceLaunchingWithR() throws Exception {
+        return PropertyUtil.getFirstApiLevel(mDevice) > 29;
+    }
+
+    private boolean isDeviceLaunchingWithS() throws Exception {
+        return PropertyUtil.getFirstApiLevel(mDevice) > 30;
+    }
+
+    private boolean isCompatiblePropertyEnforcedDevice() throws Exception {
+        return android.security.cts.SELinuxHostTest.isCompatiblePropertyEnforcedDevice(mDevice);
+    }
+
+    private boolean isSepolicySplit() throws Exception {
+        return android.security.cts.SELinuxHostTest.isSepolicySplit(mDevice);
+    }
+"""
+src_body = ""
+src_footer = """}
+"""
+
+src_method = """
+    @RestrictedBuildTest
+    public void testNeverallowRules() throws Exception {
+        String neverallowRule = "$NEVERALLOW_RULE_HERE$";
+        boolean fullTrebleOnly = $TREBLE_ONLY_BOOL_HERE$;
+        boolean launchingWithROnly = $LAUNCHING_WITH_R_ONLY_BOOL_HERE$;
+        boolean launchingWithSOnly = $LAUNCHING_WITH_S_ONLY_BOOL_HERE$;
+        boolean compatiblePropertyOnly = $COMPATIBLE_PROPERTY_ONLY_BOOL_HERE$;
+
+        if ((fullTrebleOnly) && (!isFullTrebleDevice())) {
+            // This test applies only to Treble devices but this device isn't one
+            return;
+        }
+        if ((launchingWithROnly) && (!isDeviceLaunchingWithR())) {
+            // This test applies only to devices launching with R or later but this device isn't one
+            return;
+        }
+        if ((launchingWithSOnly) && (!isDeviceLaunchingWithS())) {
+            // This test applies only to devices launching with S or later but this device isn't one
+            return;
+        }
+        if ((compatiblePropertyOnly) && (!isCompatiblePropertyEnforcedDevice())) {
+            // This test applies only to devices on which compatible property is enforced but this
+            // device isn't one
+            return;
+        }
+
+        // If sepolicy is split and vendor sepolicy version is behind platform's,
+        // only test against platform policy.
+        File policyFile =
+                (isSepolicySplit() && mVendorSepolicyVersion < mSystemSepolicyVersion) ?
+                deviceSystemPolicyFile :
+                devicePolicyFile;
+
+        /* run sepolicy-analyze neverallow check on policy file using given neverallow rules */
+        ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
+                policyFile.getAbsolutePath(), "neverallow", "-n",
+                neverallowRule);
+        pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
+        pb.redirectErrorStream(true);
+        Process p = pb.start();
+        BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
+        String line;
+        StringBuilder errorString = new StringBuilder();
+        while ((line = result.readLine()) != null) {
+            errorString.append(line);
+            errorString.append("\\n");
+        }
+        p.waitFor();
+        assertTrue("The following errors were encountered when validating the SELinux"
+                   + "neverallow rule:\\n" + neverallowRule + "\\n" + errorString,
+                   errorString.length() == 0);
+    }
+"""
diff --git a/tools/selinux/SELinuxNeverallowTestGen.py b/tools/selinux/SELinuxNeverallowTestGen.py
new file mode 100755
index 0000000..78b0631
--- /dev/null
+++ b/tools/selinux/SELinuxNeverallowTestGen.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+
+import copy
+import re
+import sys
+import SELinuxNeverallowTestFrame
+
+usage = "Usage: ./SELinuxNeverallowTestGen.py <input policy file> <output cts java source>"
+
+
+class NeverallowRule:
+    statement = ''
+    depths = None
+
+    def __init__(self, statement, depths):
+        self.statement = statement
+        self.depths = copy.deepcopy(depths)
+
+# full-Treble only tests are inside sections delimited by BEGIN_{section} and
+# END_{section} comments.
+sections = [
+    "TREBLE_ONLY",
+    "COMPATIBLE_PROPERTY_ONLY",
+    "LAUNCHING_WITH_R_ONLY",
+    "LAUNCHING_WITH_S_ONLY",
+]
+
+# extract_neverallow_rules - takes an intermediate policy file and pulls out the
+# neverallow rules by taking all of the non-commented text between the 'neverallow'
+# keyword and a terminating ';'
+# returns: a list of rules
+def extract_neverallow_rules(policy_file):
+    with open(policy_file, 'r') as in_file:
+        policy_str = in_file.read()
+
+        # uncomment section delimiter lines
+        section_headers = '|'.join(['BEGIN_%s|END_%s' % (s, s) for s in sections])
+        remaining = re.sub(
+            r'^\s*#\s*(' + section_headers + ')',
+            r'\1',
+            policy_str,
+            flags = re.M)
+        # remove comments
+        remaining = re.sub(r'#.+?$', r'', remaining, flags = re.M)
+        # match neverallow rules
+        lines = re.findall(
+            r'^\s*(neverallow\s.+?;|' + section_headers + ')',
+            remaining,
+            flags = re.M |re.S)
+
+        # extract neverallow rules from the remaining lines
+        rules = list()
+        depths = dict()
+        for section in sections:
+            depths[section] = 0
+        for line in lines:
+            is_header = False
+            for section in sections:
+                if line.startswith("BEGIN_%s" % section):
+                    depths[section] += 1
+                    is_header = True
+                    break
+                elif line.startswith("END_%s" % section):
+                    if depths[section] < 1:
+                        exit("ERROR: END_%s outside of %s section" % (section, section))
+                    depths[section] -= 1
+                    is_header = True
+                    break
+            if not is_header:
+                rule = NeverallowRule(line, depths)
+                rules.append(rule)
+
+        for section in sections:
+            if depths[section] != 0:
+                exit("ERROR: end of input while inside %s section" % section)
+
+        return rules
+
+# neverallow_rule_to_test - takes a neverallow statement and transforms it into
+# the output necessary to form a cts unit test in a java source file.
+# returns: a string representing a generic test method based on this rule.
+def neverallow_rule_to_test(rule, test_num):
+    squashed_neverallow = rule.statement.replace("\n", " ")
+    method  = SELinuxNeverallowTestFrame.src_method
+    method = method.replace("testNeverallowRules()",
+        "testNeverallowRules" + str(test_num) + "()")
+    method = method.replace("$NEVERALLOW_RULE_HERE$", squashed_neverallow)
+    for section in sections:
+        method = method.replace(
+            "$%s_BOOL_HERE$" % section,
+            "true" if rule.depths[section] else "false")
+    return method
+
+if __name__ == "__main__":
+    # check usage
+    if len(sys.argv) != 3:
+        print (usage)
+        exit(1)
+    input_file = sys.argv[1]
+    output_file = sys.argv[2]
+
+    src_header = SELinuxNeverallowTestFrame.src_header
+    src_body = SELinuxNeverallowTestFrame.src_body
+    src_footer = SELinuxNeverallowTestFrame.src_footer
+
+    # grab the neverallow rules from the policy file and transform into tests
+    neverallow_rules = extract_neverallow_rules(input_file)
+    i = 0
+    for rule in neverallow_rules:
+        src_body += neverallow_rule_to_test(rule, i)
+        i += 1
+
+    with open(output_file, 'w') as out_file:
+        out_file.write(src_header)
+        out_file.write(src_body)
+        out_file.write(src_footer)