Merge pull request #16968 from echesakovMSFT/LimitFeatureUnixAMD64StructPassingWhenTa...
[platform/upstream/coreclr.git] / netci.groovy
index 9b91a03..6b45ff7 100755 (executable)
@@ -86,6 +86,11 @@ class Constants {
                'tailcallstress'                 : ['COMPlus_TailcallStress' : '1'],
                'jitsse2only'                    : ['COMPlus_EnableAVX' : '0', 'COMPlus_EnableSSE3_4' : '0'],
                'jitnosimd'                      : ['COMPlus_FeatureSIMD' : '0'],
+               'jitincompletehwintrinsic'       : ['COMPlus_EnableIncompleteISAClass' : '1'],
+               'jitx86hwintrinsicnoavx'         : ['COMPlus_EnableIncompleteISAClass' : '1', 'COMPlus_EnableAVX' : '0'], // testing the legacy SSE encoding
+               'jitx86hwintrinsicnoavx2'        : ['COMPlus_EnableIncompleteISAClass' : '1', 'COMPlus_EnableAVX2' : '0'], // testing SNB/IVB
+               'jitx86hwintrinsicnosimd'        : ['COMPlus_EnableIncompleteISAClass' : '1', 'COMPlus_FeatureSIMD' : '0'], // match "jitnosimd", may need to remove after decoupling HW intrinsic from FeatureSIMD
+               'jitnox86hwintrinsic'            : ['COMPlus_EnableIncompleteISAClass' : '1', 'COMPlus_EnableSSE' : '0' , 'COMPlus_EnableSSE2' : '0' , 'COMPlus_EnableSSE3' : '0' , 'COMPlus_EnableSSSE3' : '0' , 'COMPlus_EnableSSE41' : '0' , 'COMPlus_EnableSSE42' : '0' , 'COMPlus_EnableAVX' : '0' , 'COMPlus_EnableAVX2' : '0' , 'COMPlus_EnableAES' : '0' , 'COMPlus_EnableBMI1' : '0' , 'COMPlus_EnableBMI2' : '0' , 'COMPlus_EnableFMA' : '0' , 'COMPlus_EnableLZCNT' : '0' , 'COMPlus_EnablePCLMULQDQ' : '0' , 'COMPlus_EnablePOPCNT' : '0'],
                'corefx_baseline'                : [ : ], // corefx baseline
                'corefx_minopts'                 : ['COMPlus_JITMinOpts' : '1'],
                'corefx_tieredcompilation'       : ['COMPlus_EXPERIMENTAL_TieredCompilation' : '1'],
@@ -130,19 +135,76 @@ class Constants {
 
     // This is the basic set of scenarios
     def static basicScenarios = [
-               'default',
+               'innerloop',
+               'normal',
                'ilrt',
                'r2r',
                'longgc',
                'formatting',
                'gcsimulator',
-               'jitdiff',
+               // 'jitdiff', // jitdiff is currently disabled, until someone spends the effort to make it fully work
                'standalone_gc',
                'gc_reliability_framework',
                'illink']
 
     def static allScenarios = basicScenarios + r2rStressScenarios.keySet() + jitStressModeScenarios.keySet()
 
+    // Valid PR trigger combinations.
+    def static prTriggeredValidInnerLoopCombos = [
+        'Windows_NT': [
+            'x64': [
+                'Checked'
+            ], 
+            'x86': [
+                'Checked',
+                'Release'
+            ], 
+            'arm': [
+                'Checked',
+            ],
+            'arm64': [
+                'Checked'
+            ],
+            'armlb': [
+                'Checked'
+            ]
+        ],
+        'Windows_NT_buildOnly': [
+            'x64': [
+                'Checked',
+                'Release'
+            ],
+            'x86': [
+                'Checked',
+                'Release'
+            ], 
+        ],
+        'Ubuntu': [
+            'x64': [
+                'Checked'
+            ],
+            'arm64': [
+                'Debug'
+            ]
+        ],
+        'CentOS7.1': [
+            'x64': [
+                'Debug',
+                'Checked'
+            ]
+        ],
+        'OSX10.12': [
+            'x64': [
+                'Checked'
+            ]
+        ],
+        'Tizen': [
+            'arm': [
+                'Checked'
+            ]
+        ],
+    ]
+
     // A set of scenarios that are valid for arm/arm64/armlb tests run on hardware. This is a map from valid scenario name
     // to Tests.lst file categories to exclude.
     //
@@ -152,9 +214,10 @@ class Constants {
     // Note that some scenarios that are commented out should be enabled, but haven't yet been.
     //
     def static validArmWindowsScenarios = [
-               'default':                                [],
+               'innerloop':                              [],
+               'normal':                                 [],
                // 'ilrt'
-               'r2r':                                    ["R2R_FAIL"],
+               'r2r':                                    ["R2R_FAIL", "R2R_EXCLUDE"],
                // 'longgc'
                // 'formatting'
                // 'gcsimulator'
@@ -162,19 +225,19 @@ class Constants {
                // 'standalone_gc'
                // 'gc_reliability_framework'
                // 'illink'
-               'r2r_jitstress1':                         ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_jitstress2':                         ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_jitstressregs1':                     ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_jitstressregs2':                     ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_jitstressregs3':                     ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_jitstressregs4':                     ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_jitstressregs8':                     ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_jitstressregs0x10':                  ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_jitstressregs0x80':                  ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_jitstressregs0x1000':                ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_jitminopts':                         ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_jitforcerelocs':                     ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
-               'r2r_gcstress15':                         ["R2R_FAIL", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_jitstress1':                         ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_jitstress2':                         ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_jitstressregs1':                     ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_jitstressregs2':                     ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_jitstressregs3':                     ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_jitstressregs4':                     ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_jitstressregs8':                     ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_jitstressregs0x10':                  ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_jitstressregs0x80':                  ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_jitstressregs0x1000':                ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_jitminopts':                         ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE", "MINOPTS_FAIL", "MINOPTS_EXCLUDE"],
+               'r2r_jitforcerelocs':                     ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"],
+               'r2r_gcstress15':                         ["R2R_FAIL", "R2R_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE", "GCSTRESS_FAIL", "GCSTRESS_EXCLUDE"],
                'minopts':                                ["MINOPTS_FAIL", "MINOPTS_EXCLUDE"],
                'tieredcompilation':                      [],
                'forcerelocs':                            [],
@@ -199,19 +262,24 @@ class Constants {
                'tailcallstress':                         ["TAILCALLSTRESS_FAIL", "TAILCALLSTRESS_EXCLUDE"],
                // 'jitsse2only'                          // Only relevant to xarch
                'jitnosimd':                              [],    // Only interesting on platforms where SIMD support exists.
-               // 'corefx_baseline'
-               // 'corefx_minopts'
-               // 'corefx_tieredcompilation'
-               // 'corefx_jitstress1'
-               // 'corefx_jitstress2'
-               // 'corefx_jitstressregs1'
-               // 'corefx_jitstressregs2'
-               // 'corefx_jitstressregs3'
-               // 'corefx_jitstressregs4'
-               // 'corefx_jitstressregs8'
-               // 'corefx_jitstressregs0x10'
-               // 'corefx_jitstressregs0x80'
-               // 'corefx_jitstressregs0x1000'
+               // 'jitincompletehwintrinsic'
+               // 'jitx86hwintrinsicnoavx'
+               // 'jitx86hwintrinsicnoavx2'
+               // 'jitx86hwintrinsicnosimd'
+               // 'jitnox86hwintrinsic'
+               'corefx_baseline':                        [],    // corefx tests don't use smarty
+               'corefx_minopts':                         [],    // corefx tests don't use smarty
+               'corefx_tieredcompilation':               [],    // corefx tests don't use smarty
+               'corefx_jitstress1':                      [],    // corefx tests don't use smarty
+               'corefx_jitstress2':                      [],    // corefx tests don't use smarty
+               'corefx_jitstressregs1':                  [],    // corefx tests don't use smarty
+               'corefx_jitstressregs2':                  [],    // corefx tests don't use smarty
+               'corefx_jitstressregs3':                  [],    // corefx tests don't use smarty
+               'corefx_jitstressregs4':                  [],    // corefx tests don't use smarty
+               'corefx_jitstressregs8':                  [],    // corefx tests don't use smarty
+               'corefx_jitstressregs0x10':               [],    // corefx tests don't use smarty
+               'corefx_jitstressregs0x80':               [],    // corefx tests don't use smarty
+               'corefx_jitstressregs0x1000':             [],    // corefx tests don't use smarty
                'gcstress0x3':                            ["GCSTRESS_FAIL", "GCSTRESS_EXCLUDE"],
                'gcstress0xc':                            ["GCSTRESS_FAIL", "GCSTRESS_EXCLUDE"],
                'zapdisable':                             ["ZAPDISABLE_FAIL", "ZAPDISABLE_EXCLUDE"],
@@ -248,6 +316,14 @@ class Constants {
                'gcstress0xc_jitstressregs0x1000':        ["GCSTRESS_FAIL", "GCSTRESS_EXCLUDE", "JITSTRESS_FAIL", "JITSTRESS_EXCLUDE"]
     ]
   
+    def static validLinuxArm64Scenarios = [ 
+        'normal',
+        'r2r',
+        'innerloop',
+        'gcstress0x3',
+        'gcstress0xc'
+    ]
+
     def static configurationList = ['Debug', 'Checked', 'Release']
 
     // This is the set of architectures
@@ -275,6 +351,7 @@ class Views {
 
 // MergeJobView: include all jobs that execute when a PR change is merged.
 Views.MergeJobView = listView('Merge') {
+    recurse()
     columns {
         status()
         weather()
@@ -288,6 +365,7 @@ Views.MergeJobView = listView('Merge') {
 
 // PeriodicJobView: include all jobs that execute on a schedule
 Views.PeriodicJobView = listView('Periodic') {
+    recurse()
     columns {
         status()
         weather()
@@ -302,6 +380,7 @@ Views.PeriodicJobView = listView('Periodic') {
 // Create a view for non-PR jobs for each architecture.
 Constants.architectureList.each { architecture ->
     Views.ArchitectureViews[architecture] = listView(architecture) {
+        recurse()
         columns {
             status()
             weather()
@@ -321,6 +400,7 @@ Constants.osList.each { os ->
         return
     }
     Views.OSViews[os] = listView(os) {
+        recurse()
         columns {
             status()
             weather()
@@ -517,11 +597,36 @@ def static isArmWindowsScenario(def scenario) {
     return Constants.validArmWindowsScenarios.containsKey(scenario)
 }
 
+def static isValidPrTriggeredInnerLoopJob(os, architecture, configuration, isBuildOnly) {
+    if (isBuildOnly == true) {
+        os = 'Windows_NT_buildOnly'
+    }
+
+    def validOsPrTriggerArchConfigs = Constants.prTriggeredValidInnerLoopCombos[os]
+
+    if (validOsPrTriggerArchConfigs == null) {
+        return false
+    }
+
+    if (validOsPrTriggerArchConfigs[architecture] != null) {
+        def validOsPrTriggerConfigs = validOsPrTriggerArchConfigs[architecture]
+
+        if (!(configuration in validOsPrTriggerConfigs)) {
+            return false
+        }
+    } else {
+        return false
+    }
+
+    return true
+}
+
 def static setJobTimeout(newJob, isPR, architecture, configuration, scenario, isBuildOnly) {
     // 2 hours (120 minutes) is the default timeout
     def timeout = 120
+    def innerLoop = (scenario == "innerloop")
 
-    if (!(scenario == 'default' && isPR == true)) {
+    if (!innerLoop) {
         // Pri-1 test builds take a long time. Default PR jobs are Pri-0; everything else is Pri-1
         // (see calculateBuildCommands()). So up the Pri-1 build jobs timeout.
         timeout = 240
@@ -536,7 +641,7 @@ def static setJobTimeout(newJob, isPR, architecture, configuration, scenario, is
             timeout = 360
         }
         else if (isJitStressScenario(scenario)) {
-            timeout = 240
+            timeout = 300
         }
         else if (isR2RBaselineScenario(scenario)) {
             timeout = 240
@@ -740,14 +845,14 @@ def static getJobName(def configuration, def architecture, def os, def scenario,
     // Need to change around some systems and other builds to pick up the right builds
     // to do that.
 
-    def suffix = scenario != 'default' ? "_${scenario}" : '';
+    def suffix = scenario != 'normal' ? "_${scenario}" : '';
     if (isBuildOnly) {
         suffix += '_bld'
     }
     def baseName = ''
     switch (architecture) {
         case 'x64':
-            if (scenario == 'default') {
+            if (scenario == 'normal') {
                 // For now we leave x64 off of the name for compatibility with other jobs
                 baseName = configuration.toLowerCase() + '_' + os.toLowerCase()
             }
@@ -809,7 +914,9 @@ def static addNonPRTriggers(def job, def branch, def isPR, def architecture, def
 
     // Check scenario.
     switch (scenario) {
-        case 'default':
+        case 'innerloop':
+            break
+        case 'normal':
             switch (architecture) {
                 case 'x64':
                 case 'x86':
@@ -977,6 +1084,11 @@ def static addNonPRTriggers(def job, def branch, def isPR, def architecture, def
         case 'tailcallstress':
         case 'jitsse2only':
         case 'jitnosimd':
+        case 'jitnox86hwintrinsic':
+        case 'jitincompletehwintrinsic':
+        case 'jitx86hwintrinsicnoavx':
+        case 'jitx86hwintrinsicnoavx2':
+        case 'jitx86hwintrinsicnosimd':
         case 'corefx_baseline':
         case 'corefx_minopts':
         case 'corefx_jitstress1':
@@ -1078,6 +1190,8 @@ def static addNonPRTriggers(def job, def branch, def isPR, def architecture, def
 // it's just used for internal builds
 // If you add a job with a trigger phrase, please add that phrase to coreclr/Documentation/project-docs/ci-trigger-phrases.md
 def static addTriggers(def job, def branch, def isPR, def architecture, def os, def configuration, def scenario, def isFlowJob, def isWindowsBuildOnlyJob) {
+    def isNormalOrInnerloop = (scenario == "normal" || scenario == "innerloop")
+    
     if (isWindowsBuildOnlyJob) {
         return
     }
@@ -1120,8 +1234,9 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
             if (scenario == 'formatting') {
                 assert configuration == 'Checked'
                 if (os == 'Windows_NT' || os == 'Ubuntu') {
-                    Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} Innerloop Formatting")
+                    Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} Formatting")
                 }
+
                 break
             }
 
@@ -1129,29 +1244,36 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                 // OpenSUSE, Debian & RedHat get trigger phrases for pri 0 build, and pri 1 build & test
                 case 'Debian8.4':
                 case 'RHEL7.2':
-                    if (scenario == 'default') {
+                    if (scenario == 'innerloop') {
                         assert !isFlowJob
-                        Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build", "(?i).*test\\W+${os}.*")
-                    }
+                        Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Innerloop Build")
+                    } 
+                    else if (scenario == 'normal') {
+                        Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build", "(?i).*test\\W+${os}\\W+${architecture}.*")
+                    }   
                     break
+
                 case 'Ubuntu16.04':
                     assert !isFlowJob
-                    assert scenario == 'default'
+                    assert scenario != 'innerloop'
                     // Distinguish with the other architectures (arm and x86)
                     Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build", "(?i).*test\\W+${os}\\W+${architecture}.*")
                     break
+
                 case 'Fedora24':
                 case 'Ubuntu16.10':
                     assert !isFlowJob
-                    assert scenario == 'default'
+                    assert scenario != 'innerloop'
                     Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build", "(?i).*test\\W+${os}\\W+.*")
                     break
+
                 case 'Ubuntu':
                     if (scenario == 'illink') {
                         Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} via ILLink", "(?i).*test\\W+${os}\\W+${architecture}\\W+${configuration}\\W+${scenario}.*")
                         break
                     }
                     // fall through
+
                 case 'OSX10.12':
                     // Triggers on the non-flow jobs aren't necessary here
                     // Corefx testing uses non-flow jobs.
@@ -1159,44 +1281,58 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                         break
                     }
                     switch (scenario) {
-                        case 'default':
+                        case 'innerloop':
+                            // PR Triggered jobs. These jobs will run pri0 tests.
+                            if (configuration == 'Checked') {
+                                Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Innerloop Build and Test")
+                            }
+                            break
+
+                        case 'normal':
                             // OSX uses checked for default PR tests
                             if (configuration == 'Checked') {
                                 // Default trigger
                                 assert !job.name.contains("centos")
-                                Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Innerloop Build and Test")
+                                Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build and Test", "(?i).*test\\W+${os}\\W+${architecture}\\W+Build and Test.*")
                             }
                             break
+
                         case 'jitdiff':
                             if (configuration == 'Checked') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Jit Diff Build and Test", "(?i).*test\\W+${os}\\W+${scenario}.*")
                             }
                             break
+
                         case 'ilrt':
                             if (configuration == 'Release') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} IL RoundTrip Build and Test", "(?i).*test\\W+${os}\\W+${scenario}.*")
                             }
                             break
+
                         case 'longgc':
                             if (configuration == 'Release') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Long-Running GC Build & Test", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
                             }
                             break
+
                         case 'gcsimulator':
                             if (configuration == 'Release') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} GC Simulator", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
                             }
                             break
+
                         case 'standalone_gc':
                             if (configuration == 'Release' || configuration == 'Checked') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Standalone GC", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
                             }
                             break
+
                         case 'gc_reliability_framework':
                             if (configuration == 'Release' || configuration == 'Checked') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} GC Reliability Framework", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
                             }
                             break
+
                         default:
                             if (isJitStressScenario(scenario)) {
                                 def displayStr = getStressModeDisplayName(scenario)
@@ -1216,12 +1352,13 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                                 assert false
                             }
                             break
+
                     }
                     break
 
                 case 'CentOS7.1':
                     switch (scenario) {
-                        case 'default':
+                        case 'innerloop':
                             // CentOS uses checked for default PR tests while debug is build only
                             if (configuration == 'Debug') {
                                 // Default trigger
@@ -1235,6 +1372,16 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Innerloop Build and Test")
                             }
                             break
+
+                        case 'normal':
+                            // Make sure this is a flow job to get build and test.
+                            if (configuration == 'Checked' && isFlowJob) {
+                                assert job.name.contains("flow")
+                                // Default trigger
+                                Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build and Test", "(?i).*test\\W+${os}\\W+${architecture}\\W+Build and Test.*")
+                            }
+                            break
+
                         default:
                             if (isR2RScenario(scenario)) {
                                 if (configuration == 'Release' || configuration == 'Checked') {
@@ -1244,50 +1391,65 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                                 }
                             }
                             break
+
                     }
                     break
 
                 case 'Windows_NT':
                     switch (scenario) {
-                        case 'default':
+                        case 'innerloop':
                             // Default trigger
-                            if (configuration == 'Checked') {
+                            if (configuration == 'Checked' || configuration == 'Release') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Innerloop Build and Test")
                             }
                             break
+
+                        case 'normal':
+                            if (configuration == 'Checked') {
+                                Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build and Test", "(?i).*test\\W+${os}\\W+${architecture}\\W+Build and Test.*")
+                            }
+                            break
+
                         case 'jitdiff':
                             if (configuration == 'Checked') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Jit Diff Build and Test", "(?i).*test\\W+${os}\\W+${scenario}.*")
                             }
                             break
+
                         case 'ilrt':
                             if (configuration == 'Release') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} IL RoundTrip Build and Test", "(?i).*test\\W+${os}\\W+${scenario}.*")
                             }
                             break
+
                         case 'longgc':
                             if (configuration == 'Release') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Long-Running GC Build & Test", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
                             }
                             break
+
                         case 'gcsimulator':
                             if (configuration == 'Release') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} GC Simulator", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
                             }
                             break
+
                         case 'standalone_gc':
                             if (configuration == 'Release' || configuration == 'Checked') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Standalone GC", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
                             }
                             break
+
                         case 'gc_reliability_framework':
                             if (configuration == 'Release' || configuration == 'Checked') {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} GC Reliability Framework", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
                             }
                             break
+
                         case 'illink':
                             Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} via ILLink", "(?i).*test\\W+${os}\\W+${architecture}\\W+${configuration}\\W+${scenario}.*")
                             break
+
                         default:
                             if (isJitStressScenario(scenario)) {
                                 def displayStr = getStressModeDisplayName(scenario)
@@ -1307,14 +1469,19 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                                 assert false
                             }
                             break
+
                     }
                     break
+
                 default:
                     println("Unknown os: ${os}");
                     assert false
                     break
+
             }
+
             break
+
         // editor brace matching: }
         case 'armlb':
         case 'arm': // editor brace matching: {
@@ -1324,7 +1491,8 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                     if (architecture == 'armlb') { // No arm legacy backend testing for Ubuntu
                         break
                     }
-                    assert scenario == 'default'
+
+                    assert scenario != 'innerloop'
                     job.with {
                         publishers {
                             azureVMAgentPostBuildAction {
@@ -1332,19 +1500,16 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                             }
                         }
                     }
-                    if (configuration == 'Debug') {
-                        Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} Cross ${configuration} Innerloop Build")
-                    }
-                    else {
-                        Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} Cross ${configuration} Build",
+                    Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} Cross ${configuration} Build",
                             "(?i).*test\\W+${os}\\W+${architecture}\\W+Cross\\W+${configuration}\\W+Build.*")
-                    }
                     break
+
                 case 'Tizen':
                     if (architecture == 'armlb') {  // No arm legacy backend testing for Tizen armel
                         break
                     }
-                    architecture='armel'
+
+                    architecture = 'armel'
                     job.with {
                         publishers {
                             azureVMAgentPostBuildAction {
@@ -1352,14 +1517,18 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                             }
                         }
                     }
-                    if (configuration == 'Checked') {
-                        Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} Cross ${configuration} Innerloop Build and Test")
+
+                    if (scenario == 'innerloop') {
+                        if (configuration == 'Checked') {
+                            Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} Cross ${configuration} Innerloop Build and Test")
+                        }
                     }
                     else {
                         Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} Cross ${configuration} Build",
                             "(?i).*test\\W+${os}\\W+${architecture}\\W+Cross\\W+${configuration}\\W+Build.*")
                     }
                     break
+
                 case 'Windows_NT':
                     // Triggers on the non-flow jobs aren't necessary here
                     if (!isFlowJob) {
@@ -1369,7 +1538,7 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                     // Set up a private trigger
                     def contextString = "${os} ${architecture} Cross ${configuration}"
                     def triggerString = "(?i).*test\\W+${os}\\W+${architecture}\\W+Cross\\W+${configuration}"
-                    if (scenario == 'default') {
+                    if (scenario == 'innerloop') {
                         contextString += " Innerloop"
                         triggerString += "\\W+Innerloop"
                     }
@@ -1389,16 +1558,15 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                     triggerString += ".*"
 
                     switch (scenario) {
-                        case 'default':
-                            // Only Checked is a default trigger.
+                        case 'innerloop':
+                            // Only Checked is an innerloop trigger.
                             if (configuration == 'Checked')
                             {
                                 Utilities.addDefaultPrivateGithubPRTriggerForBranch(job, branch, contextString, null, arm64Users)
                             }
-                            else 
-                            {
-                                Utilities.addPrivateGithubPRTriggerForBranch(job, branch, contextString, triggerString, null, arm64Users)
-                            }
+                            break
+                        case 'normal':
+                            Utilities.addPrivateGithubPRTriggerForBranch(job, branch, contextString, triggerString, null, arm64Users)
                             break
                         default:
                             // Stress jobs will use this code path.
@@ -1419,7 +1587,8 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
             // Set up a private trigger
             def contextString = "${os} ${architecture} Cross ${configuration}"
             def triggerString = "(?i).*test\\W+${os}\\W+${architecture}\\W+Cross\\W+${configuration}"
-            if (scenario == 'default') {
+
+            if (scenario == 'innerloop') {
                 contextString += " Innerloop"
                 triggerString += "\\W+Innerloop"
             }
@@ -1442,15 +1611,15 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                 case 'Ubuntu':
                 case 'Ubuntu16.04':
                     switch (scenario) {
-                        case 'default':
+                        case 'innerloop':
                             if (configuration == 'Debug' && !isFlowJob) {
                                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} Cross ${configuration} Innerloop Build")
                             }
-                            else {
-                                Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build and Test", triggerString)
-                            }
                             
                             break
+                        case 'normal':
+                            Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build and Test", triggerString)
+                            break
                         default:
                             if (isR2RScenario(scenario)) {
                                 if (configuration == 'Checked' || configuration == 'Release') {
@@ -1461,6 +1630,7 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                             break
                     }
                     break
+
                 case 'Windows_NT':
                     // Triggers on the non-flow jobs aren't necessary here
                     if (!isFlowJob) {
@@ -1469,13 +1639,14 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
 
                     assert isArmWindowsScenario(scenario)
                     switch (scenario) {
-                        case 'default':
+                        case 'innerloop':
                             if (configuration == 'Checked') {
                                 Utilities.addDefaultPrivateGithubPRTriggerForBranch(job, branch, contextString, null, arm64Users)
                             }
-                            else {
-                                Utilities.addPrivateGithubPRTriggerForBranch(job, branch, contextString, triggerString, null, arm64Users)
-                            }
+                            
+                            break
+                        case 'normal':
+                            Utilities.addPrivateGithubPRTriggerForBranch(job, branch, contextString, triggerString, null, arm64Users)
                             break
                         default:
                             // Stress jobs will use this code path.
@@ -1491,57 +1662,68 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                     break
             }
             break
+
         // editor brace matching: }
         case 'x86': // editor brace matching: {
-            assert ((os == 'Windows_NT') || ((os == 'Ubuntu') && (scenario == 'default')))
+            assert ((os == 'Windows_NT') || ((os == 'Ubuntu') && isNormalOrInnerloop))
             if (os == 'Ubuntu') {
                 // Triggers on the non-flow jobs aren't necessary here
                 if (!isFlowJob) {
                     break
                 }
+                
                 // on-demand only for ubuntu x86
                 Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build",
                     "(?i).*test\\W+${os}\\W+${architecture}\\W+${configuration}.*")
                 break
+
             }
             switch (scenario) {
-                case 'default':
-                    if (configuration == 'Checked') {
-                        assert !job.name.contains("centos")
+                case 'innerloop':
+                    if (configuration == 'Checked' || configuration == 'Release') {
                         Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Innerloop Build and Test")
                     }
-                    else {
+                    break
+
+                case 'normal':
+                    if (configuration == 'Checked') {
                         Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build and Test",
-                            "(?i).*test\\W+${os}\\W+${architecture}\\W+${configuration}.*")
+                            "(?i).*test\\W+${os}\\W+${architecture}\\W+${configuration}\\W+Build and Test.*")
                     }
                     break
+
                 case 'ilrt':
                     if (configuration == 'Release') {
                         Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} IL RoundTrip Build and Test",
                             "(?i).*test\\W+${os}\\W+${architecture}\\W+${configuration}\\W+${scenario}.*")
                     }
                     break
+
                 case 'longgc':
                     if (configuration == 'Release') {
                         Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Long-Running GC Build & Test",
                             "(?i).*test\\W+${os}\\W+${architecture}\\W+${configuration}\\W+${scenario}.*")
                     }
                     break
+
                 case 'gcsimulator':
                     if (configuration == 'Release') {
                         Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} GC Simulator",
                             "(?i).*test\\W+${os}\\W+${architecture}\\W+${configuration}\\W+${scenario}.*")
                     }
                     break
+
                 case 'standalone_gc':
                     if (configuration == 'Release' || configuration == 'Checked') {
                         Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Standalone GC",
                             "(?i).*test\\W+${os}\\W+${architecture}\\W+${configuration}\\W+${scenario}.*")
                     }
                     break
+
                 case 'illink':
                     Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} via ILLink", "(?i).*test\\W+${os}\\W+${architecture}\\W+${configuration}\\W+${scenario}.*")
                     break
+
                 default:
                     if (isJitStressScenario(scenario)) {
                         def displayStr = getStressModeDisplayName(scenario)
@@ -1560,14 +1742,16 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                         assert false
                     }
                     break
+
             }
             break
+
          // editor brace matching: }
         case 'x64_arm64_altjit':
         case 'x86_arm_altjit': // editor brace matching: {
             assert (os == 'Windows_NT')
             switch (scenario) {
-                case 'default':
+                case 'normal':
                     Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Build and Test",
                         "(?i).*test\\W+${os}\\W+${architecture}\\W+${configuration}\\W+Build and Test.*")
                     break
@@ -1577,6 +1761,7 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                     break
             }
             break
+
         // editor brace matching: }
         default:
             println("Unknown architecture: ${architecture}");
@@ -1591,7 +1776,7 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
     def lowerConfiguration = configuration.toLowerCase()
 
     def priority = '1'
-    if (scenario == 'default' && isPR == true) {
+    if (scenario == 'innerloop') {
         priority = '0'
     }
 
@@ -1771,13 +1956,13 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                         buildCommands += "powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('.\\bin\\tests\\${osGroup}.${arch}.${configuration}', '.\\bin\\tests\\tests.zip')\"";
 
                         if (!isJitStressScenario(scenario)) {
-                            // For windows, pull full test results and test drops for x86/x64.
+                            // For Windows, pull full test results and test drops for x86/x64.
                             // No need to pull for stress mode scenarios (downstream builds use the default scenario)
                             Utilities.addArchival(newJob, "bin/Product/**,bin/tests/tests.zip", "bin/Product/**/.nuget/**")
                         }
 
                         if (scenario == 'jitdiff') {
-                            // retrive jit-dasm output for base commit, and run jit-diff
+                            // retrieve jit-dasm output for base commit, and run jit-diff
                             if (!isBuildOnly) {
                                 // if this is a build only job, we want to keep the default (build) artifacts for the flow job
                                 Utilities.addArchival(newJob, "bin/tests/${osGroup}.${arch}.${configuration}/dasm/**")
@@ -1798,34 +1983,71 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
 
                     def buildArchitecture = 'arm'
 
+                    def buildOpts = ''
+
                     // For 'armlb' (the JIT LEGACY_BACKEND architecture for arm), tell build.cmd to use legacy backend for crossgen compilation.
                     // Legacy backend is not the default JIT; it is an aljit. So, this is a special case.
-                    def armCrossgenOpt = ''
                     if (architecture == 'armlb') {
-                        armCrossgenOpt = '-crossgenaltjit legacyjit.dll'
+                        buildOpts += ' -crossgenaltjit legacyjit.dll'
                     }
 
-                    // Hack: build pri1 tests for arm/armlb/arm64 build job, until we have separate pri0 and pri1 builds for the flow job to use.
-                    priority = '1'
+                    if (enableCorefxTesting) {
+                        // We shouldn't need to build the tests. However, run-corefx-tests.py currently depends on having the restored corefx
+                        // package available, to determine the correct corefx version git commit hash, and we need to build the tests before
+                        // running "tests\\runtest.cmd GenerateLayoutOnly". So build the pri-0 tests to make this happen.
+                        //
+                        // buildOpts += ' skiptests';
+                        buildOpts += " -priority=0"
+                    } else {
+                        buildOpts += " -priority=${priority}"
+                    }
 
                     // This is now a build only job. Do not run tests. Use the flow job.
-                    buildCommands += "set __TestIntermediateDir=int&&build.cmd ${lowerConfiguration} ${buildArchitecture} -priority=${priority} ${armCrossgenOpt}"
-                    
-                    // Zip up the tests directory so that we don't use so much space/time copying
-                    // 10s of thousands of files around.
-                    buildCommands += "powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('.\\bin\\tests\\${osGroup}.${buildArchitecture}.${configuration}', '.\\bin\\tests\\tests.zip')\"";
+                    buildCommands += "set __TestIntermediateDir=int&&build.cmd ${lowerConfiguration} ${buildArchitecture} ${buildOpts}"
 
-                    // Add archival.
-                    Utilities.addArchival(newJob, "bin/Product/**,bin/tests/tests.zip", "bin/Product/**/.nuget/**")
+                    if (enableCorefxTesting) {
+                        assert isBuildOnly
+                        assert architecture == 'arm'
+
+                        // Generate the test layout because it restores the corefx package which allows run-corefx-tests.py
+                        // to determine the correct matching corefx version git commit hash.
+                        buildCommands += "tests\\runtest.cmd ${lowerConfiguration} ${architecture} GenerateLayoutOnly"
+
+                        // Set the stress mode variables; this is incorporated into the generated CoreFx RunTests.cmd files.
+                        def envScriptPath = ''
+                        def buildCommandsStr = ''
+                        envScriptPath = "%WORKSPACE%\\SetStressModes.bat"
+                        buildCommandsStr += envScriptCreate(os, envScriptPath)
+                        buildCommandsStr += envScriptSetStressModeVariables(os, Constants.jitStressModeScenarios[scenario], envScriptPath)
+                        envScriptFinalize(os, envScriptPath)
+                        buildCommands += buildCommandsStr
+
+                        def workspaceRelativeFxRootLinux = "_/fx"
+                        def workspaceRelativeFxRootWin = "_\\fx"
+                        def absoluteFxRoot = "%WORKSPACE%\\_\\fx"
+
+                        buildCommands += "python -u %WORKSPACE%\\tests\\scripts\\run-corefx-tests.py -arch ${architecture} -ci_arch ${architecture} -build_type ${configuration} -fx_root ${absoluteFxRoot} -fx_branch ${branch} -env_script ${envScriptPath} -no_run_tests"
+
+                        // Zip up the CoreFx runtime and tests. We don't need the CoreCLR binaries; they have been copied to the CoreFX tree.
+                        buildCommands += "powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('${workspaceRelativeFxRootWin}\\bin\\testhost\\netcoreapp-Windows_NT-Release-arm', '${workspaceRelativeFxRootWin}\\fxruntime.zip')\"";
+                        buildCommands += "powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('${workspaceRelativeFxRootWin}\\bin\\tests', '${workspaceRelativeFxRootWin}\\fxtests.zip')\"";
+
+                        Utilities.addArchival(newJob, "${workspaceRelativeFxRootLinux}/fxruntime.zip")
+                        Utilities.addArchival(newJob, "${workspaceRelativeFxRootLinux}/fxtests.zip")
+                    } else {
+                        // Zip up the tests directory so that we don't use so much space/time copying
+                        // 10s of thousands of files around.
+                        buildCommands += "powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('.\\bin\\tests\\${osGroup}.${buildArchitecture}.${configuration}', '.\\bin\\tests\\tests.zip')\"";
+
+                        // Add archival.
+                        Utilities.addArchival(newJob, "bin/Product/**,bin/tests/tests.zip", "bin/Product/**/.nuget/**")
+                    }
                     break
                 case 'arm64':
                     assert isArmWindowsScenario(scenario)
 
                     def machineAffinityOptions = ['use_arm64_build_machine' : true]
                     setMachineAffinity(newJob, os, architecture, machineAffinityOptions)
-                   
-                    // Hack: build pri1 tests for arm/armlb/arm64 build job, until we have separate pri0 and pri1 builds for the flow job to use.
-                    priority = '1'
 
                     // This is now a build only job. Do not run tests. Use the flow job.
                     buildCommands += "set __TestIntermediateDir=int&&build.cmd ${lowerConfiguration} ${architecture} toolset_dir C:\\ats2 -priority=${priority}"
@@ -2063,7 +2285,7 @@ Constants.allScenarios.each { scenario ->
                         // Since these are just execution time differences,
                         // skip platforms that don't execute the tests here (Windows_NT only)
                         def isEnabledOS = (os == 'Windows_NT') || (os == 'Ubuntu' && isCoreFxScenario(scenario))
-                        if (!isEnabledOS || isBuildOnly) {
+                        if (!isEnabledOS) {
                             return
                         }
 
@@ -2076,11 +2298,20 @@ Constants.allScenarios.each { scenario ->
                                 if ((os == 'Ubuntu') && (architecture == 'x86')) {
                                     return
                                 }
-                                // Windows: Everything implemented
+                                if (isBuildOnly) {
+                                    return
+                                }
+                                break
+
+                            case 'arm':
+                                // We use build only jobs for Windows arm cross-compilation corefx testing, so we need to generate builds for that.
+                                if (!isBuildOnly || !isCoreFxScenario(scenario)) {
+                                    return
+                                }
                                 break
 
                             default:
-                                // arm, arm64, armlb: stress is handled through flow jobs.
+                                // arm64, armlb: stress is handled through flow jobs.
                                 return
                         }
                     }
@@ -2174,9 +2405,14 @@ Constants.allScenarios.each { scenario ->
                                     return
                                 }
                                 break
-                            case 'default':
+                            case 'normal':
                                 // Nothing skipped
                                 break
+                            case 'innerloop':
+                                if (!isValidPrTriggeredInnerLoopJob(os, architecture, configuration, isBuildOnly)) {
+                                    return
+                                }
+                                break
                             default:
                                 println("Unknown scenario: ${scenario}")
                                 assert false
@@ -2238,7 +2474,7 @@ Constants.allScenarios.each { scenario ->
                                     // Define the Windows Tests and Corefx build job names
                                     def WindowsTestsName = projectFolder + '/' +
                                                            Utilities.getFullJobName(project,
-                                                                                    getJobName(lowerConfiguration, 'x64' , 'windows_nt', 'default', true),
+                                                                                    getJobName(lowerConfiguration, 'x64' , 'windows_nt', 'normal', true),
                                                                                     false)
                                     def corefxFolder = Utilities.getFolderName('dotnet/corefx') + '/' +
                                                        Utilities.getFolderName(branch)
@@ -2289,6 +2525,8 @@ Constants.allScenarios.each { scenario ->
 
 // Create jobs requiring flow jobs. This includes x64 non-Windows, arm64 Ubuntu, and arm/arm64/armlb Windows.
 Constants.allScenarios.each { scenario ->
+    def isNormalOrInnerloop = (scenario == 'innerloop' || scenario == 'normal')
+
     [true, false].each { isPR ->
         ['arm', 'armlb', 'x64', 'arm64', 'x86'].each { architecture ->
             Constants.crossList.each { os ->
@@ -2300,8 +2538,7 @@ Constants.allScenarios.each { scenario ->
                     if (os != 'Windows_NT') {
                         return
                     }
-                }
-                else if (architecture == 'x86') {
+                } else if (architecture == 'x86') {
                     if (os != "Ubuntu") {
                         return
                     }
@@ -2325,13 +2562,13 @@ Constants.allScenarios.each { scenario ->
                     else {
                         // Non-Windows
                         if (architecture == 'arm64') {
-                            if (scenario != 'default' && scenario != 'r2r' && scenario != 'gcstress0x3' && scenario != 'gcstress0xc') {
+                            if (!(scenario in Constants.validLinuxArm64Scenarios)) {
                                 return
                             }
                         }
                         else if (architecture == 'x86') {
-                            // Linux/x86 only want default test
-                            if (scenario != 'default') {
+                            // Linux/x86 only want innerloop and default test
+                            if (!isNormalOrInnerloop) {
                                 return
                             }
                         }
@@ -2342,7 +2579,7 @@ Constants.allScenarios.each { scenario ->
                         if (configuration != 'Checked' && configuration != 'Release') {
                             return
                         }
-                        if (scenario != 'default' && !isR2RScenario(scenario) && !isJitStressScenario(scenario)) {
+                        if (!isNormalOrInnerloop && !isR2RScenario(scenario) && !isJitStressScenario(scenario)) {
                             return
                         }
                     }
@@ -2352,7 +2589,7 @@ Constants.allScenarios.each { scenario ->
                         if (configuration != 'Release') {
                             return
                         }
-                        if (scenario != 'default') {
+                        if (!isNormalOrInnerloop) {
                             return
                         }
                     }
@@ -2363,8 +2600,9 @@ Constants.allScenarios.each { scenario ->
                         if (configuration != 'Checked') {
                             return
                         }
-                        // CoreFx JIT stress tests currently not implemented for flow jobs.
-                        if (isCoreFxScenario(scenario)) {
+
+                        // CoreFx JIT stress tests currently only implemented for ARM.
+                        if (isCoreFxScenario(scenario) && (architecture != 'arm')) {
                             return
                         }
                     }
@@ -2390,17 +2628,20 @@ Constants.allScenarios.each { scenario ->
                                     return
                                 }
                                 break
+
                             case 'jitdiff':
                                 if (configuration != 'Checked') {
                                     return;
                                 }
                                 break
+
                             case 'gc_reliability_framework':
                             case 'standalone_gc':
                                 if (configuration != 'Release' && configuration != 'Checked') {
                                     return
                                 }
                                 break
+
                             case 'formatting':
                                 return
                             case 'illink':
@@ -2408,9 +2649,18 @@ Constants.allScenarios.each { scenario ->
                                     return
                                 }
                                 break
-                            case 'default':
+
+                            case 'normal':
+                                // Nothing skipped
+                                break
+
+                            case 'innerloop':
                                 // Nothing skipped
+                                if (!isValidPrTriggeredInnerLoopJob(os, architecture, configuration, false)) {
+                                    return
+                                }
                                 break
+
                             default:
                                 println("Unknown scenario: ${scenario}")
                                 assert false
@@ -2420,133 +2670,144 @@ Constants.allScenarios.each { scenario ->
 
                     // Done filtering. Now, create the jobs.
 
+                    // =============================================================================================
+                    // Create the test job
+                    // =============================================================================================
+
+                    def windowsArmJob = (os == "Windows_NT" && architecture in validWindowsNTCrossArches)
+
                     def lowerConfiguration = configuration.toLowerCase()
                     def osGroup = getOSGroup(os)
                     def jobName = getJobName(configuration, architecture, os, scenario, false) + "_tst"
 
-                    def inputCoreCLRBuildName = projectFolder + '/' +
-                        Utilities.getFullJobName(project, getJobName(configuration, architecture, os, 'default', false), isPR)
-
-                    // If this is a stress scenario, there isn't any difference in the build job, so we didn't create a build only
-                    // job for Windows_NT specific to that stress mode. Just copy from the default scenario.
-                    def testBuildScenario = scenario
-                    if (isJitStressScenario(testBuildScenario) || isR2RScenario(testBuildScenario) || isLongGc(testBuildScenario)) {
-                        testBuildScenario = 'default'
-                    }
-
-                    def inputWindowsTestBuildArch = architecture
-                    if (architecture == "arm64" && os != "Windows_NT") {
-                        // Use the x64 test build for arm64 unix
-                        inputWindowsTestBuildArch = "x64"
+                    def inputCoreCLRBuildScenario = scenario == 'innerloop' ? 'innerloop' : 'normal'
+                    def inputCoreCLRBuildIsBuildOnly = false
+                    if (isCoreFxScenario(scenario)) {
+                        // Every CoreFx test depends on its own unique build.
+                        inputCoreCLRBuildScenario = scenario
+                        inputCoreCLRBuildIsBuildOnly = true
                     }
+                    def inputCoreCLRFolderName = getJobFolder(inputCoreCLRBuildScenario)
+                    def inputCoreCLRBuildName = projectFolder + '/' +
+                        Utilities.getFullJobName(project, getJobName(configuration, architecture, os, inputCoreCLRBuildScenario, inputCoreCLRBuildIsBuildOnly), isPR, inputCoreCLRFolderName)
 
                     def inputWindowsTestsBuildName = ""
+                    if (windowsArmJob != true) {
+                        // If this is a stress scenario, there isn't any difference in the build job, so we didn't create a build only
+                        // job for Windows_NT specific to that stress mode. Just copy from the default scenario.
 
-                    if (isJitStressScenario(scenario)) {
-                        inputWindowsTestsBuildName = projectFolder + '/' +
-                            Utilities.getFullJobName(project, getJobName(configuration, inputWindowsTestBuildArch, 'windows_nt', testBuildScenario, false), isPR)
-                    } else {
-                        inputWindowsTestsBuildName = projectFolder + '/' +
-                            Utilities.getFullJobName(project, getJobName(configuration, inputWindowsTestBuildArch, 'windows_nt', testBuildScenario, true), isPR)
+                        def testBuildScenario = scenario == 'innerloop' ? 'innerloop' : 'normal'
 
-                    }
+                        def inputWindowsTestBuildArch = architecture
+                        if (architecture == "arm64" && os != "Windows_NT") {
+                            // Use the x64 test build for arm64 unix
+                            inputWindowsTestBuildArch = "x64"
+                        }
 
-                     
-                    // Enable Server GC for Ubuntu PR builds
-                    def serverGCString = ''
-                    if (os == 'Ubuntu' && isPR) {
-                        serverGCString = '--useServerGC'
-                    }
+                        if (isJitStressScenario(scenario)) {
+                            inputWindowsTestsBuildName = projectFolder + '/' +
+                                Utilities.getFullJobName(project, getJobName(configuration, inputWindowsTestBuildArch, 'windows_nt', testBuildScenario, false), isPR)
+                        } else {
+                            inputWindowsTestsBuildName = projectFolder + '/' +
+                                Utilities.getFullJobName(project, getJobName(configuration, inputWindowsTestBuildArch, 'windows_nt', testBuildScenario, true), isPR)
+                        }
+                    } // if (windowsArmJob != true)
 
+                    def serverGCString = ''
                     def testOpts = ''
 
-                    if (isR2RScenario(scenario)) {
+                    if (windowsArmJob != true) {
+                        // Enable Server GC for Ubuntu PR builds
+                        if (os == 'Ubuntu' && isPR) {
+                            serverGCString = '--useServerGC'
+                        }
 
-                        testOpts += ' --crossgen --runcrossgentests'
+                        if (isR2RScenario(scenario)) {
 
-                        if (scenario == 'r2r_jitstress1') {
-                            testOpts += ' --jitstress=1'
-                        }
-                        else if (scenario == 'r2r_jitstress2') {
-                            testOpts += ' --jitstress=2'
-                        }
-                        else if (scenario == 'r2r_jitstressregs1') {
-                            testOpts += ' --jitstressregs=1'
-                        }
-                        else if (scenario == 'r2r_jitstressregs2') {
-                            testOpts += ' --jitstressregs=2'
-                        }
-                        else if (scenario == 'r2r_jitstressregs3') {
-                            testOpts += ' --jitstressregs=3'
-                        }
-                        else if (scenario == 'r2r_jitstressregs4') {
-                            testOpts += ' --jitstressregs=4'
-                        }
-                        else if (scenario == 'r2r_jitstressregs8') {
-                            testOpts += ' --jitstressregs=8'
-                        }
-                        else if (scenario == 'r2r_jitstressregs0x10') {
-                            testOpts += ' --jitstressregs=0x10'
-                        }
-                        else if (scenario == 'r2r_jitstressregs0x80') {
-                            testOpts += ' --jitstressregs=0x80'
-                        }
-                        else if (scenario == 'r2r_jitstressregs0x1000') {
-                            testOpts += ' --jitstressregs=0x1000'
-                        }
-                        else if (scenario == 'r2r_jitminopts') {
-                            testOpts += ' --jitminopts'
-                        }
-                        else if (scenario == 'r2r_jitforcerelocs') {
-                            testOpts += ' --jitforcerelocs'
-                        }
-                        else if (scenario == 'r2r_gcstress15') {
-                            testOpts += ' --gcstresslevel=0xF'
-                        }
-                    }
-                    else if (scenario == 'jitdiff') {
-                        testOpts += ' --jitdisasm --crossgen'
-                    }
-                    else if (scenario == 'illink') {
-                        testOpts += ' --link=\$WORKSPACE/linker/linker/bin/netcore_Release/netcoreapp2.0/ubuntu-x64/publish/illink'
-                    }
-                    else if (isLongGc(scenario)) {
-                        // Long GC tests behave very poorly when they are not
-                        // the only test running (many of them allocate until OOM).
-                        testOpts += ' --sequential'
+                            testOpts += ' --crossgen --runcrossgentests'
 
-                        // A note - runtest.sh does have "--long-gc" and "--gcsimulator" options
-                        // for running long GC and GCSimulator tests, respectively. We don't use them
-                        // here because using a playlist file produces much more readable output on the CI machines
-                        // and reduces running time.
-                        //
-                        // The Long GC playlist contains all of the tests that are
-                        // going to be run. The GCSimulator playlist contains all of
-                        // the GC simulator tests.
-                        if (scenario == 'longgc') {
-                            testOpts += ' --long-gc --playlist=./tests/longRunningGcTests.txt'
+                            if (scenario == 'r2r_jitstress1') {
+                                testOpts += ' --jitstress=1'
+                            }
+                            else if (scenario == 'r2r_jitstress2') {
+                                testOpts += ' --jitstress=2'
+                            }
+                            else if (scenario == 'r2r_jitstressregs1') {
+                                testOpts += ' --jitstressregs=1'
+                            }
+                            else if (scenario == 'r2r_jitstressregs2') {
+                                testOpts += ' --jitstressregs=2'
+                            }
+                            else if (scenario == 'r2r_jitstressregs3') {
+                                testOpts += ' --jitstressregs=3'
+                            }
+                            else if (scenario == 'r2r_jitstressregs4') {
+                                testOpts += ' --jitstressregs=4'
+                            }
+                            else if (scenario == 'r2r_jitstressregs8') {
+                                testOpts += ' --jitstressregs=8'
+                            }
+                            else if (scenario == 'r2r_jitstressregs0x10') {
+                                testOpts += ' --jitstressregs=0x10'
+                            }
+                            else if (scenario == 'r2r_jitstressregs0x80') {
+                                testOpts += ' --jitstressregs=0x80'
+                            }
+                            else if (scenario == 'r2r_jitstressregs0x1000') {
+                                testOpts += ' --jitstressregs=0x1000'
+                            }
+                            else if (scenario == 'r2r_jitminopts') {
+                                testOpts += ' --jitminopts'
+                            }
+                            else if (scenario == 'r2r_jitforcerelocs') {
+                                testOpts += ' --jitforcerelocs'
+                            }
+                            else if (scenario == 'r2r_gcstress15') {
+                                testOpts += ' --gcstresslevel=0xF'
+                            }
                         }
-                        else if (scenario == 'gcsimulator') {
-                            testOpts += ' --gcsimulator --playlist=./tests/gcSimulatorTests.txt'
+                        else if (scenario == 'jitdiff') {
+                            testOpts += ' --jitdisasm --crossgen'
                         }
-                    }
-                    else if (isGcReliabilityFramework(scenario)) {
-                        testOpts += ' --build-overlay-only'
-                    }
-                    else if (scenario == 'standalone_gc') {
-                        if (osGroup == 'OSX') {
-                            testOpts += ' --gcname=libclrgc.dylib'
+                        else if (scenario == 'illink') {
+                            testOpts += ' --link=\$WORKSPACE/linker/linker/bin/netcore_Release/netcoreapp2.0/ubuntu-x64/publish/illink'
                         }
-                        else if (osGroup == 'Linux') {
-                            testOpts += ' --gcname=libclrgc.so'
+                        else if (isLongGc(scenario)) {
+                            // Long GC tests behave very poorly when they are not
+                            // the only test running (many of them allocate until OOM).
+                            testOpts += ' --sequential'
+
+                            // A note - runtest.sh does have "--long-gc" and "--gcsimulator" options
+                            // for running long GC and GCSimulator tests, respectively. We don't use them
+                            // here because using a playlist file produces much more readable output on the CI machines
+                            // and reduces running time.
+                            //
+                            // The Long GC playlist contains all of the tests that are
+                            // going to be run. The GCSimulator playlist contains all of
+                            // the GC simulator tests.
+                            if (scenario == 'longgc') {
+                                testOpts += ' --long-gc --playlist=./tests/longRunningGcTests.txt'
+                            }
+                            else if (scenario == 'gcsimulator') {
+                                testOpts += ' --gcsimulator --playlist=./tests/gcSimulatorTests.txt'
+                            }
                         }
-                        else {
-                            println("Unexpected OS group: ${osGroup} for os ${os}")
-                            assert false
+                        else if (isGcReliabilityFramework(scenario)) {
+                            testOpts += ' --build-overlay-only'
                         }
-                    }
-
-                    def windowsArmJob = (os == "Windows_NT" && architecture in validWindowsNTCrossArches)
+                        else if (scenario == 'standalone_gc') {
+                            if (osGroup == 'OSX') {
+                                testOpts += ' --gcname=libclrgc.dylib'
+                            }
+                            else if (osGroup == 'Linux') {
+                                testOpts += ' --gcname=libclrgc.so'
+                            }
+                            else {
+                                println("Unexpected OS group: ${osGroup} for os ${os}")
+                                assert false
+                            }
+                        }
+                    } // if (windowsArmJob != true)
 
                     def folder = getJobFolder(scenario)
                     def newJob = job(Utilities.getFullJobName(project, jobName, isPR, folder)) {
@@ -2559,7 +2820,7 @@ Constants.allScenarios.each { scenario ->
                         }
                         else {
                             parameters {
-                                stringParam('CORECLR_WINDOWS_BUILD', '', 'Build number to copy CoreCLR windows test binaries from')
+                                stringParam('CORECLR_WINDOWS_BUILD', '', 'Build number to copy CoreCLR Windows test binaries from')
                                 stringParam('CORECLR_BUILD', '', "Build number to copy CoreCLR ${osGroup} binaries from")
                             }
                         }
@@ -2580,7 +2841,7 @@ Constants.allScenarios.each { scenario ->
 
                             // Coreclr build we are trying to test
                             //
-                            //  ** NOTE ** This will, correctly, overwrite over the CORE_ROOT from the windows test archive
+                            //  ** NOTE ** This will, correctly, overwrite the CORE_ROOT from the Windows test archive
 
                             copyArtifacts(inputCoreCLRBuildName) {
                                 excludePatterns('**/testResults.xml', '**/*.ni.dll')
@@ -2668,53 +2929,71 @@ Constants.allScenarios.each { scenario ->
                                     shell("${dockerCmd}./tests/scripts/run-gc-reliability-framework.sh ${architecture} ${configuration}")
                                 }
                             } 
-
                             else { // windowsArmJob == true
-                                // Unzip tests.
-                                batchFile("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('bin\\tests\\tests.zip', 'bin\\tests\\${osGroup}.${architecture}.${configuration}')")
                                 
-                                // Build the build commands
-                                def buildCommands = ""
-                                
-                                def coreRootLocation = "%WORKSPACE%\\bin\\tests\\Windows_NT.${architecture}.${configuration}\\Tests\\Core_Root"
-                                def addEnvVariable =  { variable, value -> buildCommands += "set ${variable}=${value}\r\n"}
-                                def addCommand = { cmd -> buildCommands += "${cmd}\r\n"}
+                                if (isCoreFxScenario(scenario)) {
 
-                                // Make sure Command Extensions are enabled. Used so %ERRORLEVEL% is available.
-                                addCommand("SETLOCAL ENABLEEXTENSIONS")
-    
-                                // For all jobs 
-                                addEnvVariable("CORE_ROOT", coreRootLocation)
-
-                                addEnvVariable("COMPlus_NoGuiOnAssert", "1")
-                                addEnvVariable("COMPlus_ContinueOnAssert", "0")
-
-                                // ARM legacy backend; this is an altjit.
-                                if (architecture == "armlb") {
-                                    addEnvVariable("COMPlus_AltJit", "*")
-                                    addEnvVariable("COMPlus_AltJitNgen", "*")
-                                    addEnvVariable("COMPlus_AltJitName", "legacyjit.dll")
-                                    addEnvVariable("COMPlus_AltJitAssertOnNYI", "1")
-                                }
+                                    // Only arm supported for corefx testing now.
+                                    assert architecture == 'arm'
 
-                                // If we are running a stress mode, we'll set those variables as well
-                                if (isJitStressScenario(scenario) || isR2RStressScenario(scenario)) {
-                                    def stressValues = null
-                                    if (isJitStressScenario(scenario)) {
-                                        stressValues = Constants.jitStressModeScenarios[scenario]
-                                    }
-                                    else {
-                                        stressValues = Constants.r2rStressScenarios[scenario]
+                                    // Unzip CoreFx runtime
+                                    batchFile("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('_\\fx\\fxruntime.zip', '_\\fx\\bin\\testhost\\netcoreapp-Windows_NT-Release-arm')")
+
+                                    // Unzip CoreFx tests.
+                                    batchFile("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('_\\fx\\fxtests.zip', '_\\fx\\bin\\tests')")
+
+                                    // Add the script to run the corefx tests
+                                    def corefx_runtime_path   = "%WORKSPACE%\\_\\fx\\bin\\testhost\\netcoreapp-Windows_NT-Release-arm"
+                                    def corefx_tests_dir      = "%WORKSPACE%\\_\\fx\\bin\\tests"
+                                    def corefx_exclusion_file = "%WORKSPACE%\\tests\\arm\\corefx_test_exclusions.txt"
+                                    batchFile("call %WORKSPACE%\\tests\\scripts\\run-corefx-tests.bat ${corefx_runtime_path} ${corefx_tests_dir} ${corefx_exclusion_file}")
+
+                                } else { // !isCoreFxScenario(scenario)
+
+                                    // Unzip tests.
+                                    batchFile("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('bin\\tests\\tests.zip', 'bin\\tests\\${osGroup}.${architecture}.${configuration}')")
+
+                                    def buildCommands = ""
+
+                                    def coreRootLocation = "%WORKSPACE%\\bin\\tests\\Windows_NT.${architecture}.${configuration}\\Tests\\Core_Root"
+                                    def addEnvVariable =  { variable, value -> buildCommands += "set ${variable}=${value}\r\n"}
+                                    def addCommand = { cmd -> buildCommands += "${cmd}\r\n"}
+
+                                    // Make sure Command Extensions are enabled. Used so %ERRORLEVEL% is available.
+                                    addCommand("SETLOCAL ENABLEEXTENSIONS")
+        
+                                    // For all jobs 
+                                    addEnvVariable("CORE_ROOT", coreRootLocation)
+
+                                    addEnvVariable("COMPlus_NoGuiOnAssert", "1")
+                                    addEnvVariable("COMPlus_ContinueOnAssert", "0")
+
+                                    // ARM legacy backend; this is an altjit.
+                                    if (architecture == "armlb") {
+                                        addEnvVariable("COMPlus_AltJit", "*")
+                                        addEnvVariable("COMPlus_AltJitNgen", "*")
+                                        addEnvVariable("COMPlus_AltJitName", "legacyjit.dll")
+                                        addEnvVariable("COMPlus_AltJitAssertOnNYI", "1")
                                     }
 
-                                    stressValues.each { key, value -> 
-                                        addEnvVariable(key, value)
+                                    // If we are running a stress mode, we'll set those variables as well
+                                    if (isJitStressScenario(scenario) || isR2RStressScenario(scenario)) {
+                                        def stressValues = null
+                                        if (isJitStressScenario(scenario)) {
+                                            stressValues = Constants.jitStressModeScenarios[scenario]
+                                        }
+                                        else {
+                                            stressValues = Constants.r2rStressScenarios[scenario]
+                                        }
+
+                                        stressValues.each { key, value -> 
+                                            addEnvVariable(key, value)
+                                        }
                                     }
-                                }
 
-                                if (isR2RScenario(scenario)) {
-                                    // Crossgen the framework assemblies.
-                                    buildCommands += """
+                                    if (isR2RScenario(scenario)) {
+                                        // Crossgen the framework assemblies.
+                                        buildCommands += """
 @for %%F in (%CORE_ROOT%\\*.dll) do @call :PrecompileAssembly "%CORE_ROOT%" "%%F" %%~nxF
 @goto skip_PrecompileAssembly
 
@@ -2738,78 +3017,73 @@ Constants.allScenarios.each { scenario ->
 :skip_PrecompileAssembly
 """
 
-                                    // Set RunCrossGen variable to cause test wrappers to invoke their logic to run
-                                    // crossgen on tests before running them.
-                                    addEnvVariable("RunCrossGen", "true")
-                                }
-
-                                // Create the smarty command
-                                def smartyCommand = "C:\\Tools\\Smarty.exe /noecid /noie /workers 9 /inc EXPECTED_PASS "
-                                def addSmartyFlag = { flag -> smartyCommand += flag + " "}
-                                def addExclude = { exclude -> addSmartyFlag("/exc " + exclude)}
+                                        // Set RunCrossGen variable to cause test wrappers to invoke their logic to run
+                                        // crossgen on tests before running them.
+                                        addEnvVariable("RunCrossGen", "true")
+                                    } // isR2RScenario(scenario)
 
-                                def addArchSpecificExclude = { architectureToExclude, exclude -> if (architectureToExclude == "armlb") { addExclude("LEGACYJIT_" + exclude) } else { addExclude(exclude) } }
+                                    // Create the smarty command
+                                    def smartyCommand = "C:\\Tools\\Smarty.exe /noecid /noie /workers 9 /inc EXPECTED_PASS "
+                                    def addSmartyFlag = { flag -> smartyCommand += flag + " "}
+                                    def addExclude = { exclude -> addSmartyFlag("/exc " + exclude)}
 
-                                if (architecture == "armlb") {
-                                    addExclude("LEGACYJIT_FAIL")
-                                }
+                                    def addArchSpecificExclude = { architectureToExclude, exclude -> if (architectureToExclude == "armlb") { addExclude("LEGACYJIT_" + exclude) } else { addExclude(exclude) } }
 
-                                if (isJitStressScenario(scenario) || isR2RStressScenario(scenario)) {
-                                    def failTag = "JITSTRESS_FAIL"
-                                    def excludeTag = "JITSTRESS_EXCLUDE"
+                                    if (architecture == "armlb") {
+                                        addExclude("LEGACYJIT_FAIL")
+                                    }
 
-                                    if (scenario.contains('gc')) {
-                                        failTag = "GCSTRESS_FAIL"
-                                        excludeTag = "GCSTRESS_EXCLUDE"
+                                    // Exclude tests based on scenario.
+                                    Constants.validArmWindowsScenarios[scenario].each { excludeTag ->
+                                        addArchSpecificExclude(architecture, excludeTag)
                                     }
 
-                                    addArchSpecificExclude(architecture, failTag)
-                                    addArchSpecificExclude(architecture, excludeTag)
-                                }
-                                else {
-                                    addExclude("pri1")
-                                }
+                                    // Innerloop jobs run Pri-0 tests; everyone else runs Pri-1.
+                                    if (scenario == 'innerloop') {
+                                        addExclude("pri1")
+                                    }
 
-                                // Exclude any test marked LONG_RUNNING; these often exceed the standard timeout and fail as a result.
-                                // TODO: We should create a "long running" job that runs these with a longer timeout.
-                                addExclude("LONG_RUNNING")
+                                    // Exclude any test marked LONG_RUNNING; these often exceed the standard timeout and fail as a result.
+                                    // TODO: We should create a "long running" job that runs these with a longer timeout.
+                                    addExclude("LONG_RUNNING")
 
-                                smartyCommand += "/lstFile Tests.lst"
+                                    smartyCommand += "/lstFile Tests.lst"
 
-                                def testListArch = [
-                                    'arm64': 'arm64',
-                                    'arm': 'arm',
-                                    'armlb': 'arm'
-                                ]
+                                    def testListArch = [
+                                        'arm64': 'arm64',
+                                        'arm': 'arm',
+                                        'armlb': 'arm'
+                                    ]
 
-                                def archLocation = testListArch[architecture]
+                                    def archLocation = testListArch[architecture]
 
-                                addCommand("copy %WORKSPACE%\\tests\\${archLocation}\\Tests.lst bin\\tests\\${osGroup}.${architecture}.${configuration}")
-                                addCommand("pushd bin\\tests\\${osGroup}.${architecture}.${configuration}")
-                                addCommand("${smartyCommand}")
+                                    addCommand("copy %WORKSPACE%\\tests\\${archLocation}\\Tests.lst bin\\tests\\${osGroup}.${architecture}.${configuration}")
+                                    addCommand("pushd bin\\tests\\${osGroup}.${architecture}.${configuration}")
+                                    addCommand("${smartyCommand}")
 
-                                // Save the errorlevel from the smarty command to be used as the errorlevel of this batch file.
-                                // However, we also need to remove all the variables that were set during this batch file, so we
-                                // can run the ZIP powershell command (below) in a clean environment. (We can't run the powershell
-                                // command with the COMPlus_AltJit variables set, for example.) To do that, we do ENDLOCAL as well
-                                // as save the current errorlevel on the same line. This works because CMD evaluates the %errorlevel%
-                                // variable expansion (or any variable expansion on the line) BEFORE it executes the ENDLOCAL command.
-                                // Note that the ENDLOCAL also undoes the pushd command, but we add the popd here for clarity.
-                                addCommand("popd & ENDLOCAL & set __save_smarty_errorlevel=%errorlevel%")
+                                    // Save the errorlevel from the smarty command to be used as the errorlevel of this batch file.
+                                    // However, we also need to remove all the variables that were set during this batch file, so we
+                                    // can run the ZIP powershell command (below) in a clean environment. (We can't run the powershell
+                                    // command with the COMPlus_AltJit variables set, for example.) To do that, we do ENDLOCAL as well
+                                    // as save the current errorlevel on the same line. This works because CMD evaluates the %errorlevel%
+                                    // variable expansion (or any variable expansion on the line) BEFORE it executes the ENDLOCAL command.
+                                    // Note that the ENDLOCAL also undoes the pushd command, but we add the popd here for clarity.
+                                    addCommand("popd & ENDLOCAL & set __save_smarty_errorlevel=%errorlevel%")
 
-                                // ZIP up the smarty output, no matter what the smarty result.
-                                addCommand("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('.\\bin\\tests\\${osGroup}.${architecture}.${configuration}\\Smarty.run.0', '.\\bin\\tests\\${osGroup}.${architecture}.${configuration}\\Smarty.run.0.zip')\"")
+                                    // ZIP up the smarty output, no matter what the smarty result.
+                                    addCommand("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('.\\bin\\tests\\${osGroup}.${architecture}.${configuration}\\Smarty.run.0', '.\\bin\\tests\\${osGroup}.${architecture}.${configuration}\\Smarty.run.0.zip')\"")
 
-                                addCommand("echo %errorlevel%")
-                                addCommand("dir .\\bin\\tests\\${osGroup}.${architecture}.${configuration}")
+                                    addCommand("echo %errorlevel%")
+                                    addCommand("dir .\\bin\\tests\\${osGroup}.${architecture}.${configuration}")
 
-                                // Use the smarty errorlevel as the script errorlevel.
-                                addCommand("exit /b %__save_smarty_errorlevel%")
+                                    // Use the smarty errorlevel as the script errorlevel.
+                                    addCommand("exit /b %__save_smarty_errorlevel%")
 
-                                batchFile(buildCommands)
-                            }
-                        }
-                    }
+                                    batchFile(buildCommands)
+                                } // non-corefx testing
+                            } // windowsArmJob == true
+                        } // steps
+                    } // job
 
                     addToViews(newJob, isPR, architecture, os)
 
@@ -2847,22 +3121,29 @@ Constants.allScenarios.each { scenario ->
                         Utilities.addXUnitDotNETResults(newJob, '**/coreclrtests.xml')
                     }
                     else {
-                        Utilities.addArchival(newJob, "bin/tests/${osGroup}.${architecture}.${configuration}/Smarty.run.0/*.smrt", '', true, false)
+                        if (!isCoreFxScenario(scenario)) {
+                            Utilities.addArchival(newJob, "bin/tests/${osGroup}.${architecture}.${configuration}/Smarty.run.0/*.smrt", '', true, false)
 
-                        // Archive a ZIP file of the entire Smarty.run.0 directory. This is possibly a little too much,
-                        // but there is no easy way to only archive the HTML/TXT files of the failing tests, so we get
-                        // all the passing test info as well. Not necessarily a bad thing, but possibly somewhat large.
-                        Utilities.addArchival(newJob, "bin/tests/${osGroup}.${architecture}.${configuration}/Smarty.run.0.zip", '', true, false)
+                            // Archive a ZIP file of the entire Smarty.run.0 directory. This is possibly a little too much,
+                            // but there is no easy way to only archive the HTML/TXT files of the failing tests, so we get
+                            // all the passing test info as well. Not necessarily a bad thing, but possibly somewhat large.
+                            Utilities.addArchival(newJob, "bin/tests/${osGroup}.${architecture}.${configuration}/Smarty.run.0.zip", '', true, false)
+                        }
                     }
 
+                    // =============================================================================================
                     // Create a build flow to join together the build and tests required to run this test.
+                    // =============================================================================================
+
                     // Windows CoreCLR build and Linux CoreCLR build (in parallel) ->
                     // Linux CoreCLR test
                     def flowJobName = getJobName(configuration, architecture, os, scenario, false) + "_flow"
                     def fullTestJobName = projectFolder + '/' + newJob.name
                     // Add a reference to the input jobs for report purposes
                     JobReport.Report.addReference(inputCoreCLRBuildName)
-                    JobReport.Report.addReference(inputWindowsTestsBuildName)
+                    if (windowsArmJob != true) {
+                        JobReport.Report.addReference(inputWindowsTestsBuildName)
+                    }
                     JobReport.Report.addReference(fullTestJobName)
                     def newFlowJob = null
 
@@ -2877,7 +3158,7 @@ Constants.allScenarios.each { scenario ->
                         // The product build supports building and archiving the tests.
 
                         newFlowJob = buildFlowJob(Utilities.getFullJobName(project, flowJobName, isPR, folder)) {
-                        buildFlow("""
+                        buildFlow("""\
 coreclrBuildJob = build(params, '${inputCoreCLRBuildName}')
 
 // And then build the test build
@@ -2887,7 +3168,7 @@ build(params + [CORECLR_BUILD: coreclrBuildJob.build.number], '${fullTestJobName
                     }
                     else {
                         newFlowJob = buildFlowJob(Utilities.getFullJobName(project, flowJobName, isPR, folder)) {
-                        buildFlow("""
+                        buildFlow("""\
 // Build the input jobs in parallel
 parallel (
 { coreclrBuildJob = build(params, '${inputCoreCLRBuildName}') },