Repository: buildtesters/buildtest (language: python)

Issue:
add date for v1.3 release



Discussion / hints:

Fix patch:
diff --git a/.coveragerc b/.coveragerc
index 455120991..f52da7368 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,6 +1,9 @@
 [run]
 branch = True
+parallel = True
+concurrency = multiprocessing
 source = buildtest
+
 omit =
     buildtest/main.py
     buildtest/defaults.py
diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
index 1d39f0ae4..8915af44c 100644
--- a/.github/ISSUE_TEMPLATE/bug.yml
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -83,4 +83,4 @@ body:
         We encourage you provide a detailed explanation of the issue including link to output, logfile, error message and screenshots in order for us troubleshoot the issue.
         If you have a quick question please post your question in `#general` channel on [buildtest slack](https://hpcbuildtest.slack.com/) first.
 
-        Thanks for taking the time to contribute back to buildtest.
\ No newline at end of file
+        Thanks for taking the time to contribute back to buildtest.
diff --git a/.github/ISSUE_TEMPLATE/cli_feature.yml b/.github/ISSUE_TEMPLATE/cli_feature.yml
index dd65f6464..5e5c60727 100644
--- a/.github/ISSUE_TEMPLATE/cli_feature.yml
+++ b/.github/ISSUE_TEMPLATE/cli_feature.yml
@@ -27,4 +27,4 @@ body:
       label: Is there an existing issue
       options:
         - label: I confirm there is no existing [issue](https://github.com/buildtesters/buildtest/issues) for this issue
-          required: true
\ No newline at end of file
+          required: true
diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml
index 45549c146..1a073e944 100644
--- a/.github/ISSUE_TEMPLATE/documentation.yml
+++ b/.github/ISSUE_TEMPLATE/documentation.yml
@@ -16,7 +16,7 @@ body:
   - type: textarea
     id: description
     attributes:
-      label:  Issue Description
+      label: Issue Description
       description: Please provide text and explanation of the issue
     validations:
       required: true
@@ -33,4 +33,4 @@ body:
       label: Is there an existing issue
       options:
         - label: I confirm there is no existing [issue](https://github.com/buildtesters/buildtest/issues) for this issue
-          required: true
\ No newline at end of file
+          required: true
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 6b5bec233..349a05ef0 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -21,4 +21,3 @@ updates:
       time: "02:00"
     labels:
       - "pip dependencies"
-
diff --git a/.github/workflows/build_container.yml b/.github/workflows/build_container.yml
index 9325510f8..3eaa124d0 100644
--- a/.github/workflows/build_container.yml
+++ b/.github/workflows/build_container.yml
@@ -2,7 +2,7 @@ name: Build Container
 
 on:
   pull_request:
-    branches: [ devel ]
+    branches: [devel]
 
 jobs:  
   build_container:
@@ -29,8 +29,8 @@ jobs:
           password: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Build and push Docker image
-        uses: docker/build-push-action@v3
+        uses: docker/build-push-action@v4
         with:
           tags: ghcr.io/buildtesters/buildtest_spack:latest
           context: scripts/spack_container
-          push: true
\ No newline at end of file
+          push: true
diff --git a/.github/workflows/installation.yml b/.github/workflows/installation.yml
index 0cb68c3ad..5d9680935 100644
--- a/.github/workflows/installation.yml
+++ b/.github/workflows/installation.yml
@@ -2,7 +2,7 @@ name: installation
 
 on:
   pull_request:
-    branches: [ devel ]
+    branches: [devel]
 
 
 jobs:
@@ -13,8 +13,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ macos-latest, ubuntu-latest ]
-        python-version: [ '3.7', '3.8', '3.9', '3.10' ]
+        os: [macos-latest, ubuntu-latest]
+        python-version: ['3.7', '3.8', '3.9', '3.10']
 
     steps:
     - uses: actions/checkout@v3
@@ -26,11 +26,11 @@ jobs:
 
     - name: Buildtest Installation for os - ${{ matrix.os }}, python version - ${{ matrix.python-version }}
       env:
-        OS: ${{ matrix.os }}
+        OS: '${{ matrix.os }}'
       shell: bash
       run: |
         if [ $OS = "ubuntu-latest" ] ; then sudo apt-get install -y tcsh zsh; else brew install tcsh zsh ;  fi
-        echo $SHELL
+        echo "$SHELL"
         $SHELL --version
         git --version
         pip install -U pip
diff --git a/.github/workflows/jsonschemadocs.yml b/.github/workflows/jsonschemadocs.yml
index 6d59c93a8..ae53ea279 100644
--- a/.github/workflows/jsonschemadocs.yml
+++ b/.github/workflows/jsonschemadocs.yml
@@ -44,40 +44,40 @@ jobs:
     needs: jsonschemadocs
     runs-on: ubuntu-latest
     steps:
-
-     - name: Checkout 🛎️
-       uses: actions/checkout@v3 # If you're using actions/checkout@v3 you must set persist-credentials to false in most cases for the deployment to work correctly.
-       with:
+      - name: Checkout 🛎️
+        # If you're using actions/checkout@v3 you must set persist-credentials
+        # to false in most cases for the deployment to work correctly.
+        uses: actions/checkout@v3
+        with:
           persist-credentials: false
 
-     - name: Download Schemas
-       uses: actions/download-artifact@v3
-       with:
-         name: schemafiles
-         path: downloads/schemas
-
-     - name: Download Schema Examples
-       uses: actions/download-artifact@v3
-       with:
-         name: schemaexamples
-         path: downloads/examples
-
-     - name: Download Schema Docs
-       uses: actions/download-artifact@v3
-       with:
+      - name: Download Schemas
+        uses: actions/download-artifact@v3
+        with:
+          name: schemafiles
+          path: downloads/schemas
+
+      - name: Download Schema Examples
+        uses: actions/download-artifact@v3
+        with:
+          name: schemaexamples
+          path: downloads/examples
+
+      - name: Download Schema Docs
+        uses: actions/download-artifact@v3
+        with:
           name: schemadocs
           path: downloads/schemadocs
 
-     - name: Display content of downloaded directory
-       run: |
-         ls -R downloads
-
-
-     - name: Deploy to GitHub Pages 🚀
-       uses: JamesIves/github-pages-deploy-action@v4.4.1
-       with:
-         # ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
-         branch: gh-pages 
-         folder: downloads 
-         target-folder: pages
-         clean: true
+      - name: Display content of downloaded directory
+        run: |
+          ls -R downloads
+
+      - name: Deploy to GitHub Pages 🚀
+        uses: JamesIves/github-pages-deploy-action@v4.4.1
+        with:
+          # ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
+          branch: gh-pages
+          folder: downloads
+          target-folder: pages
+          clean: true
diff --git a/.github/workflows/nightly_regression.yml b/.github/workflows/nightly_regression.yml
new file mode 100644
index 000000000..77ca9dd0e
--- /dev/null
+++ b/.github/workflows/nightly_regression.yml
@@ -0,0 +1,50 @@
+name: Nightly Regression Test
+
+on:
+ 
+  schedule:
+    - cron: '0 0 * * *'
+
+jobs:
+
+  buildtest_nightly_regression_test:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: true
+
+      matrix:
+        os: [macos-latest, ubuntu-latest]
+        python-version: ['3.7', '3.8', '3.9', '3.10']
+
+    steps:
+    - uses: actions/checkout@v3
+
+    - name: Setup Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v4.5.0
+      with:
+        python-version: ${{ matrix.python-version }}
+
+    - name: Run buildtest unittests for os - ${{ matrix.os }}, python version - ${{ matrix.python-version }}
+      env:
+        OS: ${{ matrix.os }}
+      shell: bash
+      run: |
+        if [ $OS = "ubuntu-latest"  ] ; then sudo apt-get install -y csh tcsh zsh && sudo add-apt-repository ppa:kelleyk/emacs && sudo apt-get install -y emacs28; else brew install tcsh zsh emacs ;  fi
+        cat /etc/shells
+        source setup.sh
+        pip install pytest
+        pip install coverage
+        python $BUILDTEST_ROOT/buildtest/tools/unittests.py -c
+        returncode=$?
+        if [ $returncode != 0 ]; then exit $returncode; fi
+        buildtest cdash upload --site=github nightly-$(date)
+
+    - name: Setup tmate session
+      if: ${{ failure() }}
+      uses: mxschmitt/action-tmate@v3
+      timeout-minutes: 30
+
+    - name: Upload coverage to Codecov
+      uses: codecov/codecov-action@v3
+      with:
+        verbose: true
diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml
index 8f7726215..d3f6096ab 100644
--- a/.github/workflows/regression.yml
+++ b/.github/workflows/regression.yml
@@ -2,7 +2,7 @@ name: regressiontest
 
 on:
   pull_request:
-    branches: [ devel ]
+    branches: [devel]
     paths:
       - 'buildtest/**'
       - 'tests/**'
diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml
new file mode 100644
index 000000000..faa221e4c
--- /dev/null
+++ b/.github/workflows/shellcheck.yml
@@ -0,0 +1,21 @@
+name: Shell Check
+
+on:
+  push:
+    branches:
+      - devel
+  pull_request:
+    branches:
+      - devel
+
+jobs:
+  style_checks:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout Repository
+        uses: actions/checkout@v3
+      - name: Run Shell Check
+        run: |
+          sudo apt-get install -y shellcheck
+          shellcheck --version
+          shellcheck -x  setup.sh scripts/spack_container/*.sh
diff --git a/.github/workflows/black.yml b/.github/workflows/style.yml
similarity index 66%
rename from .github/workflows/black.yml
rename to .github/workflows/style.yml
index 80398d98a..6249cc75d 100644
--- a/.github/workflows/black.yml
+++ b/.github/workflows/style.yml
@@ -1,4 +1,4 @@
-name: Black Formatter
+name: Style Checks
 
 on:
   push:
@@ -6,12 +6,12 @@ on:
       - master
       - devel
   pull_request:
-      branches:
-        - master
-        - devel
+    branches:
+      - master
+      - devel
 
 jobs:
-  blackformat:
+  style_checks:
     runs-on: ubuntu-latest
     steps:
 
@@ -20,17 +20,27 @@ jobs:
       - uses: actions/setup-python@v4.5.0
         with:
           python-version: 3.8
-      - uses: jamescurtin/isort-action@master
+
+      - name: Check imports with pyflakes
+        run: |
+          pip install pyflakes
+          pyflakes buildtest tests scripts
+
+      - name: Running isort check
+        uses: jamescurtin/isort-action@master
         with:
           #requirementsFiles: "requirements.txt docs/requirements.txt"
           sortPaths: "buildtest tests docs"
+
+      - name: Running black check
       # use black github action see https://black.readthedocs.io/en/stable/integrations/github_actions.html
-      - uses: psf/black@stable
+        uses: psf/black@stable
         with:
           options: "--check --verbose"
           src: "buildtest tests scripts"
+          version: "~= 22.0"
 
-      - name: Check imports with pyflakes
+      - name: Run yamllint
         run: |
-          pip install pyflakes
-          pyflakes buildtest tests scripts
+          pip install yamllint
+          yamllint -c .yamllint.yml .github .gitlab buildtest tests docs
diff --git a/.gitlab/ascent.yml b/.gitlab/ascent.yml
index 83188f1d8..0eb5628e0 100644
--- a/.gitlab/ascent.yml
+++ b/.gitlab/ascent.yml
@@ -31,4 +31,3 @@ ascent_pr_regression_test:
     - codecov -t $CODECOV_TOKEN
     - source deactivate
     - rm -rf $CI_PROJECT_DIR/.conda
-
diff --git a/.gitlab/nersc.yml b/.gitlab/nersc.yml
index 7b812c88d..49f16202a 100644
--- a/.gitlab/nersc.yml
+++ b/.gitlab/nersc.yml
@@ -55,4 +55,4 @@ perlmutter_regression_test:
     # CODECOV_TOKEN environment must be set, this value is stored in CI/CD variable at https://software.nersc.gov/NERSC/buildtest/-/settings/ci_cd
     - codecov -t $CODECOV_TOKEN
     - source deactivate
-    - conda env remove -p $CI_PROJECT_DIR/.conda -y
\ No newline at end of file
+    - conda env remove -p $CI_PROJECT_DIR/.conda -y
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 930f556e6..916287b73 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,3 +1,8 @@
+ci:
+    autoupdate_branch: ''
+    autoupdate_commit_msg: '[pre-commit.ci] pre-commit weekly autoupdate'
+    autoupdate_schedule: weekly
+
 repos:
   - repo: https://github.com/pycqa/isort
     rev: 5.9.3
@@ -12,4 +17,3 @@ repos:
         language_version: python3
 
 
-
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 0eb74447e..b00a82265 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -19,9 +19,6 @@ build:
     - zsh
     - tcsh
 
-formats:
-  - pdf
-
 python:
   install:
     - requirements: requirements.txt
diff --git a/.shellcheckrc b/.shellcheckrc
new file mode 100644
index 000000000..d081b85a2
--- /dev/null
+++ b/.shellcheckrc
@@ -0,0 +1,1 @@
+disable=SC1091,SC1130,SC2016,SC2296
diff --git a/.yamllint.yml b/.yamllint.yml
new file mode 100644
index 000000000..3a1732001
--- /dev/null
+++ b/.yamllint.yml
@@ -0,0 +1,32 @@
+yaml-files:
+  - '*.yaml'
+  - '*.yml'
+  - '.yamllint'
+
+rules:
+  braces: enable
+  brackets: enable
+  colons: enable
+  commas: enable
+  comments: disable
+  comments-indentation: disable
+  document-end: disable
+  document-start: disable
+  empty-lines: enable
+  empty-values: disable
+  float-values: disable
+  hyphens: enable
+  indentation:
+    indent-sequences: whatever
+  key-duplicates: enable
+  key-ordering: disable
+  line-length:
+    max: 80
+    level: warning
+  new-line-at-end-of-file: enable
+  new-lines: enable
+  octal-values: disable
+  quoted-strings: disable
+  trailing-spaces: disable
+  truthy: disable
+
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d7b062f8a..c81ea63d4 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,29 @@
 CHANGELOG
 =========
 
+v1.3 (Mar 7th, 2023)
+----------------------
+
+**Command Line Updates:**
+
+- Add option ``buildtest inspect query --theme`` to select the color theme. `#1383 <https://github.com/buildtesters/buildtest/pull/1383>`_
+- Add option ``buildtest buildspec find --row-count`` to report number of rows for for a buildspec query. `#1377 <https://github.com/buildtesters/buildtest/pull/1377>`_
+- Add option ``buildtest build --limit`` to limit the number of tests to run. `#1381 <https://github.com/buildtesters/buildtest/pull/1381>`_
+
+**Buildspec Updates:**
+
+- Add Assert Less Than performance check via property ``assert_lt``. `#1382 <https://github.com/buildtesters/buildtest/pull/1382>`_
+- Add Symbolic Link check via property ``is_symlink``. `#1394 <https://github.com/buildtesters/buildtest/pull/1394>`_
+- Add support for running regular expression on arbitrary files via property ``file_regex``. `#1396 <https://github.com/buildtesters/buildtest/pull/1396>`_
+- Add support for file count via property ``file_count`` that can perform directory traversal. This also includes traversal by file extension, regular expression. We can filter by file type (file, dir, symlink) and control traversal depth. `#1405 <https://github.com/buildtesters/buildtest/pull/1405>`_
+
+**Misc Updates:**
+
+- Added precommit ci for auto-updating **.pre-commit-config.yaml** `#1389 <https://github.com/buildtesters/buildtest/pull/1389>`_
+- Add a nightly regression test github workflow. `#1384 <https://github.com/buildtesters/buildtest/pull/1384>`_, `#1378 <https://github.com/buildtesters/buildtest/pull/1378>`_
+- Add `yamllint <https://yamllint.readthedocs.io/en/stable/>`_ and `shellcheck <https://github.com/koalaman/shellcheck>`_ check for lint check on YAML and shell scripts. Update contributing guide and fix all yamllint issues `#1385 <https://github.com/buildtesters/buildtest/pull/1385>`_, `#1390 <https://github.com/buildtesters/buildtest/pull/1390>`_, `#1404 <https://github.com/buildtesters/buildtest/pull/1404>`_
+- Fix issue where coverage was not tracked with with multiprocessing library that caused lack of test coverage. `#1408 <https://github.com/buildtesters/buildtest/pull/1408>`_
+
 v1.2 (Feb 6th, 2023)
 ----------------------
 
diff --git a/README.rst b/README.rst
index bbaddab17..6d5952fe9 100644
--- a/README.rst
+++ b/README.rst
@@ -1,10 +1,10 @@
 .. image:: https://github.com/buildtesters/buildtest/blob/devel/logos/BuildTest_Primary_Center_4x3.png
    :width: 400
    
-.. |docs| image:: https://readthedocs.org/projects/buildtest/badge/?version=latest
+.. |docs| image:: https://readthedocs.org/projects/buildtest/badge/?version=devel
     :alt: Documentation Status
     :scale: 100%
-    :target: https://buildtest.readthedocs.io/en/latest/?badge=latest
+    :target: https://readthedocs.org/projects/buildtest/builds/
 
 .. |license| image:: https://img.shields.io/github/license/buildtesters/buildtest.svg
 
@@ -36,7 +36,7 @@
 .. |checkurls| image:: https://github.com/buildtesters/buildtest/workflows/Check%20URLs/badge.svg
     :target: https://github.com/buildtesters/buildtest/actions
 
-.. |blackformat| image:: https://github.com/buildtesters/buildtest/workflows/Black%20Formatter/badge.svg
+.. |style| image:: https://github.com/buildtesters/buildtest/workflows/Style%20Checks/badge.svg
     :target: https://github.com/buildtesters/buildtest/actions
     
 .. |isort| image:: https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336
@@ -48,6 +48,9 @@
 .. |regressiontest| image:: https://github.com/buildtesters/buildtest/workflows/regressiontest/badge.svg
     :target: https://github.com/buildtesters/buildtest/actions
 
+.. |nightly_regression_test| image:: https://github.com/buildtesters/buildtest/actions/workflows/nightly_regression.yml/badge.svg
+    :target: https://github.com/buildtesters/buildtest/actions
+
 .. |gh_pages_devel| image:: https://github.com/buildtesters/buildtest/workflows/Schema%20Documentation/badge.svg
     :target: https://github.com/buildtesters/buildtest/actions
 
@@ -57,7 +60,8 @@
 buildtest
 ---------
 
-| |license| |docs| |codecov| |release| |installation| |regressiontest| |gh_pages_devel| |checkurls| |codefactor| |blackformat|  |black| |isort| |issues| |open_pr| |commit_activity_yearly| |commit_activity_monthly| |core_infrastructure| |zenodo|
+| |license| |docs| |codecov| |release| |installation| |regressiontest| |nightly_regression_test| |gh_pages_devel| |checkurls| |codefactor| |style|  |black| |isort| |issues| |open_pr| |commit_activity_yearly| |commit_activity_monthly| |core_infrastructure| |zenodo|
+=======
 
 `buildtest <https://buildtest.rtfd.io/>`_ is a testing framework that automates build/execution of tests which allows HPC facilities to develop
 test for their system. In buildtest, tests are written as test recipes called **Buildspecs** which is a `YAML <https://yaml.org/>`_
diff --git a/bash_completion.sh b/bash_completion.sh
index 5ba8321e7..fe92f058b 100644
--- a/bash_completion.sh
+++ b/bash_completion.sh
@@ -138,7 +138,7 @@ _buildtest ()
   #case "${prev}" in
     build|bd)
       local shortoption="-b -e -et -f -m -s -t -u -x"
-      local longoption="--buildspec --disable-executor-check --executor --executor-type --exclude --filter --helpfilter --maxpendtime --modules --module-purge --nodes --pollinterval --procs --rerun --remove-stagedir --retry --stage --tags --timeout --unload-modules"
+      local longoption="--buildspec --disable-executor-check --executor --executor-type --exclude --filter --helpfilter --limit --maxpendtime --modules --module-purge --nodes --pollinterval --procs --rerun --remove-stagedir --retry --stage --tags --timeout --unload-modules"
       local allopts="${longoption} ${shortoption}"
 
       COMPREPLY=( $( compgen -W "$allopts" -- $cur ) )
@@ -197,10 +197,6 @@ _buildtest ()
     report|rt)
       local opts="--end --fail --filter --filterfields --format --formatfields --help --helpfilter --helpformat --latest --no-header --oldest --pager --pass --row-count --start --terse  -e -f -h -n -p -s -t c clear l list p path sm summary"
       COMPREPLY=( $( compgen -W "${opts}" -- $cur ) )
-      case ${prev} in --color)
-        COMPREPLY=( $( compgen -W "$(_supported_colors)" -- $cur ) )
-        return
-      esac
       case "${prev}" in --filter)
         COMPREPLY=( $( compgen -W "$(_avail_report_filterfields)" -- $cur ) )
         return
@@ -241,7 +237,7 @@ _buildtest ()
           local opts="-h --help"
           COMPREPLY=( $( compgen -W "${opts}" -- $cur ) );;
         view|v)
-          local opts="--help --pager --theme -h -p -t"
+          local opts="--help --pager --theme -h -t"
           COMPREPLY=( $( compgen -W "${opts}" -- $cur ) )
 
           case "${prev}" in --theme|-t)
@@ -259,13 +255,13 @@ _buildtest ()
       # case statement to handle completion for buildtest inspect [name|id|list] command
       case "${COMP_WORDS[2]}" in
         list|l)
-          local opts="--builder --help --no-header --terse -b -h -n -t"
+          local opts="--builder --help --no-header --pager --terse -b -h -n -t"
           COMPREPLY=( $( compgen -W "${opts}" -- $cur ) );;
         name|n)
-          COMPREPLY=( $( compgen -W "$(_builder_names)" -- $cur ) )
+          COMPREPLY=( $( compgen -W "$(_test_name)" -- $cur ) )
 
           if [[ $cur == -* ]] ; then
-            local opts="--all --help -a -h"
+            local opts="--all --help --pager -a -h"
             COMPREPLY=( $( compgen -W "${opts}" -- $cur ) )
           fi
           ;;
@@ -273,16 +269,20 @@ _buildtest ()
           COMPREPLY=( $( compgen -W "$(_test_buildspec)" -- $cur ) )
 
           if [[ $cur == -* ]] ; then
-            local opts="--all --help -a -h"
+            local opts="--all --help --pager -a -h"
             COMPREPLY=( $( compgen -W "${opts}" -- $cur ) )
           fi
           ;;
         query|q)
           COMPREPLY=( $( compgen -W "$(_builder_names)" -- $cur ) )
           if [[ $cur == -* ]] ; then
-            local opts="--buildscript --buildenv --error --help --output --testpath -b -be -e -o -h -o -t"
+            local opts="--buildscript --buildenv --error --help --output --testpath --theme -b -be -e -o -h -o -t"
             COMPREPLY=( $( compgen -W "${opts}" -- $cur ) )
           fi
+          case "${prev}" in --theme)
+            COMPREPLY=( $( compgen -W "$(_avail_color_themes)" -- $cur ) )
+            return
+          esac
           ;;
       esac
       ;;
@@ -301,7 +301,7 @@ _buildtest ()
            COMPREPLY=( $( compgen -W "${opts}" -- $cur ) );;
          # completion for rest of arguments
          *)
-           local longopts="--buildspec --count --executors --filter --filterfields --format --formatfields --group-by-executor --group-by-tags --help --helpfilter --helpformat --no-header --pager --paths --quiet --rebuild --tags --root --terse"
+           local longopts="--buildspec --count --executors --filter --filterfields --format --formatfields --group-by-executor --group-by-tags --help --helpfilter --helpformat --no-header --pager --paths --quiet --rebuild --row-count --tags --root --terse"
            local shortopts="-b -e -h -n -p -q -r -t"
            local subcmds="invalid"
            local allopts="${longopts} ${shortopts} ${subcmds}"
@@ -314,10 +314,6 @@ _buildtest ()
              COMPREPLY=( $( compgen -W "$(_avail_buildspec_formatfields)" -- $cur ) )
              return
            esac
-           case ${prev} in --color)
-            COMPREPLY=( $( compgen -W "$(_supported_colors)" -- $cur ) )
-            return
-           esac
            ;;
          esac
         ;;
@@ -325,8 +321,8 @@ _buildtest ()
          case ${COMP_WORDS[3]} in
          # completion for rest of arguments
          *)
-           local longopts="--pager"
-           local shortopts="-p"
+           local longopts="--help --pager"
+           local shortopts="-h"
            local allopts="${longopts} ${shortopts}"
            COMPREPLY=( $( compgen -W "${allopts}" -- $cur ) );;
          esac
@@ -384,17 +380,13 @@ _buildtest ()
       ;;
 
     history|hy)
-      local cmds="--help -h list query"
+      local cmds="--help --pager -h list query"
       COMPREPLY=( $( compgen -W "${cmds}" -- $cur ) )
 
       case ${COMP_WORDS[2]} in
       list)
-        local opts="--help --no-header --pager --terse -h -n -t"
+        local opts="--help --no-header --terse -h -n -t"
         COMPREPLY=( $( compgen -W "${opts}" -- $cur ) )
-
-        if [[ "${prev}" == "--color" ]]; then
-          COMPREPLY=( $( compgen -W "$(_supported_colors)" -- $cur ) )
-        fi
         ;;
       query)
         local opts="--help --log --output -h -l -o"
@@ -442,7 +434,7 @@ _buildtest ()
     *)
       local cmds="build buildspec cd cdash clean config debugreport docs help info inspect history path report schema schemadocs stats stylecheck tutorial-examples unittests"
       local alias_cmds="bd bc cg debug it h hy rt style test"
-      local opts="--color --config --debug --editor --help --helpcolor --logpath --loglevel --print-log --no-color --report --version --view-log -c -d -h -l -r -V"
+      local opts="--color --config --debug --editor --help --helpcolor --logpath --loglevel --print-log --no-color --report --version --view-log -c -d -h -l -p -r -V"
 
       case "${cur}" in
       # print main options to buildtest
diff --git a/docs/__init__.py b/docs/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/buildspecs/buildspec_overview.rst b/docs/buildspecs/buildspec_overview.rst
index f599854b6..df0f5e7c1 100644
--- a/docs/buildspecs/buildspec_overview.rst
+++ b/docs/buildspecs/buildspec_overview.rst
@@ -240,10 +240,12 @@ Currently, we can match state based on the following:
 
   - :ref:`Return Code <returncode>`
   - :ref:`Runtime <runtime>`
-  - :ref:`Regular Expression <regex>`
+  - :ref:`Regular Expression on output/error file <regex>`
+  - :ref:`Regular Expression on arbitrary file <file_regex>`
   - :ref:`Performance Check <perf_checks>`
   - :ref:`Explicit Test Status <explicit_status>`
   - :ref:`File Checks <file_checks>`
+  - :ref:`Symbolic Link Check <symlink_check>`
 
 .. _returncode:
 
@@ -314,6 +316,31 @@ a 0. Take a close look at the **status** property
 
    .. command-output:: buildtest build -b tutorials/test_status/status_regex.yml
 
+.. _file_regex:
+
+Specify Regular Expression on arbitrary filenames
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the previous example, we applied :ref:`regular expression on output or error file <regex>`; however, you may want to apply
+regular expression on arbitrary files. This can be done by specifying ``file_regex`` property. The ``file_regex`` property is
+an array of assertions, where each assertion must have ``filename`` and ``exp`` property. The ``filename`` property is the path
+to filename and ``exp`` is the regular expression you want to apply.
+
+In this example, we have three tests that make use of ``file_regex`` property. The first test will perform a regular expression
+on multiple file names. The second test will attempt to check on a directory name which is not supported since regular expression must
+be applied to file name. The third test will show that variable expansion and environment variable expansion is supported.
+
+.. literalinclude:: ../tutorials/test_status/regex_on_filename.yml
+   :language: yaml
+   :emphasize-lines: 9-14,23-26,35-40
+
+We can build this test by running the following command:
+
+.. dropdown:: ``buildtest build -b tutorials/test_status/regex_on_filename.yml``
+
+   .. command-output:: buildtest build -b tutorials/test_status/regex_on_filename.yml
+
+
 .. _runtime:
 
 Passing Test based on runtime
@@ -377,7 +404,14 @@ If we build this test, we expect buildtest to honor the value of ``state`` prope
 File Checks
 ~~~~~~~~~~~~~
 
-buildtest supports various file checks that can be used as means for passing test.
+buildtest supports various file checks that can be used as means for passing test. This can include
+checking for :ref:`file_existence`, :ref:`file_and_directory_check`, :ref:`symlink_check`, and :ref:`file_count`.
+
+.. _file_existence:
+
+File Existence
+~~~~~~~~~~~~~~~
+
 
 For instance, if you want to check for file existence, you can use  ``exists`` property
 which expects a list of file or directory names to check. This can be useful if your test
@@ -431,6 +465,11 @@ Let's validate and build this test.
 
     .. command-output:: buildtest build -b tutorials/test_status/file_exists_with_number.yml
 
+.. _file_and_directory_check:
+
+File and Directory Checks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 In the next example, we introduce checks for files and directory via ``is_file`` and
 ``is_dir`` property, which behaves similar to ``exists`` except they will check if each item
 is a file or directory. We expect the first test to fail, because **$HOME/.bashrc** is
@@ -447,6 +486,108 @@ Let's build the test and see the output.
 
    .. command-output:: buildtest build -b tutorials/test_status/file_and_dir_check.yml
 
+.. _symlink_check:
+
+Symbolic Link Check
+~~~~~~~~~~~~~~~~~~~~
+
+buildtest can configure PASS/FAIL of test based on the status of symbolic link. This can be useful if your test will create a symbolic link to a file or directory and 
+test will pass if the symbolic link is present.  
+
+You can use the ``is_symlink`` property which expects a list of values that are checked for symbolic links. In the example below, the test will pass as all the values are valid symbolic links and are not broken. Note that variable and shell expansion
+is supported.
+
+.. literalinclude:: ../tutorials/test_status/is_symlink.yml
+   :language: yaml
+   :emphasize-lines: 9-13
+
+We can run this test by running the following.
+
+.. dropdown:: ``buildtest build -b tutorials/test_status/is_symlink.yml``
+
+   .. command-output:: buildtest build -b tutorials/test_status/is_symlink.yml
+
+.. _file_count:
+
+File Count
+~~~~~~~~~~~~
+
+buildtest can check for number of files in a directory. This can be useful if your test writes number of files and you
+want to check if the number of files is as expected. You can use the ``file_count`` property to perform file count. This
+property is a list of assertion, where each item is an object. The ``dir`` and ``count`` are required keys.
+
+The ``dir`` is the path to directory to perform directory traversal, and ``count`` key is the number of expected files that will be
+used for comparison. In the first test, we perform a directory walk and expect 5 files in the directory. We can perform directory
+search based on file extension by using ``ext`` key. The ``ext`` property can be a string or a list. The second test will perform
+directory walk on directory named **foo** and search for file extensions **.sh**, **.py**, **.txt**. The ``depth`` property controls
+the maximum depth for directory traversal, this must be of an integer type. The ``depth`` property is optional and if not specified, we will
+perform full directory traversal.
+
+.. literalinclude:: ../tutorials/test_status/file_count.yml
+   :language: yaml
+   :emphasize-lines: 9-12,21-29
+
+
+We can run this test by running the following.
+
+.. dropdown:: ``buildtest build -b tutorials/test_status/file_count.yml``
+
+   .. command-output:: buildtest build -b tutorials/test_status/file_count.yml
+
+In the next example, we introduce ``filepattern`` property which allows you to check for files based on a pattern. The ``filepattern`` property
+is a regular expression which is compiled via `re.compile <https://docs.python.org/3/library/re.html#re.compile>`_ and applied to a
+directory path. Please note the regular expression must be valid, otherwise buildtest will not return any files during directory traversal.
+
+You can use ``filepattern`` and ``ext`` property together to search for files. If both are specified, then we will search for files
+with both methods and join the two list prior to performing comparison.
+
+.. literalinclude:: ../tutorials/test_status/file_count_pattern.yml
+   :language: yaml
+   :emphasize-lines: 14-22,32-37
+
+
+Let's build this test by running the following:
+
+.. dropdown:: ``buildtest build -b tutorials/test_status/file_count_pattern.yml``
+
+   .. command-output:: buildtest build -b tutorials/test_status/file_count_pattern.yml
+
+In the next example, we will introduce ``filetype`` property that can be used to filter directory search based on file type.
+The ``filetype`` property can one of the following values **file**, **dir**, **symlink**. Once set, the directory traversal will
+seek out files based on the file type. Note that when ``filetype`` is set to ``dir`` we will return the parent directory and
+all sub-directories. This test will create a few subdirectories and create symbolic link, next we will perform directory search
+by directory and symbolic link. We expect this test to pass as we will find 3 directories and 2 symbolic links.
+
+.. literalinclude:: ../tutorials/test_status/file_count_filetype.yml
+   :language: yaml
+   :emphasize-lines: 11-18
+
+Let's build this test by running the following:
+
+.. dropdown:: ``buildtest build -b tutorials/test_status/file_count_filetype.yml``
+
+   .. command-output:: buildtest build -b tutorials/test_status/file_count_filetype.yml
+
+Buildtest will perform a directory walk when using ``file_count``, which can run into performance issues if you have a large directory.
+We have added a safety check during directory traversal to a maximum of **999999** files. You have the option to configure the directory
+traversal limit using ``file_traversal_limit`` which is an integer, the default value is **10000** if not specified. The minimum value and
+maximum value can be 1 and 999999 respectively.
+
+In this next example, we will illustrate how this feature works. We will create 99 *.txt* files in directory **foo**. We will
+perform two assertions with different values for ``file_traversal_limit``. In the first example, we will set this to 50 and
+expect 50 files returned. We expect this check to be **True**. In the next example, we will set ``file_traversal_limit`` to 20 and
+set ``count: 10`` where we should expect a failure. In principle, we should retrieve 20 files but this will mismatch the comparison check.
+
+.. literalinclude:: ../tutorials/test_status/file_count_file_traverse_limit.yml
+   :language: yaml
+   :emphasize-lines: 9-16
+
+We can try building this test by running the following:
+
+.. dropdown:: ``buildtest build -b tutorials/test_status/file_count_file_traverse_limit.yml``
+
+   .. command-output:: buildtest build -b tutorials/test_status/file_count_file_traverse_limit.yml
+
 Skipping test
 -------------
 
diff --git a/docs/buildspecs/compiler.rst b/docs/buildspecs/compiler.rst
index a7e6d1941..575f2ac06 100644
--- a/docs/buildspecs/compiler.rst
+++ b/docs/buildspecs/compiler.rst
@@ -315,6 +315,7 @@ variables in the script that is mapped to each compiler
 - **BUILDTEST_FC**: Path to Fortran compiler
 - **BUILDTEST_CFLAGS**: C compiler flags
 - **BUILDTEST_CXXFLAGS**: C++ compiler flags
+- **BUILDTEST_FFLAGS**: Fortran compiler flags
 - **BUILDTEST_CPPFLAGS**: C++ Pre Preprocessor flags
 - **BUILDTEST_LDFLAGS**: Linker Flags
 
diff --git a/docs/command_line_tutorial.rst b/docs/command_line_tutorial.rst
index 0fcd7e31d..c5683c290 100644
--- a/docs/command_line_tutorial.rst
+++ b/docs/command_line_tutorial.rst
@@ -257,7 +257,7 @@ We have an alias ``buildtest cg`` for **buildtest config** command. If you want
 
     .. command-output:: buildtest cg view
 
-We also support color themes (``buildtest cg view --theme <theme>``) and paging ``buildtest cg view --pager``.
+We also support color themes (``buildtest cg view --theme <theme>``) when showing content of buildtest configuration.
 
 Buildtest configuration file defines one or more :ref:`executors <configuring_executors>` that are used when
 writing test. Every test must be run by an executor. To retrieve all executors in a flat-listing you can run::
diff --git a/docs/conferences.rst b/docs/conferences.rst
index caca68814..e9df0fa24 100644
--- a/docs/conferences.rst
+++ b/docs/conferences.rst
@@ -10,7 +10,7 @@ Talks
     :header: "Conference", "Talk", "Date", "Link"
     :widths:  40, 50, 20, 20
 
-    "ECP Project Tutorial", "**Automated Acceptance Testing in HPC with buildtest**", "Feb 7th, 2023", "`PPTX <https://docs.google.com/presentation/d/1zKm-AMLciBwCJOmZdNaCfqP36BAkl2OL/edit?rtpof=true>`_, `PDF <https://drive.google.com/file/d/1CnPH3Ct_YF8H7bLHx0X84TMf3mu_HKBz/view?usp=share_link>`_"
+    "ECP Project Tutorial", "**Automated Acceptance Testing in HPC with buildtest**", "Feb 7th, 2023", "`PPTX <https://docs.google.com/presentation/d/1zKm-AMLciBwCJOmZdNaCfqP36BAkl2OL/edit?rtpof=true>`_, `PDF <https://drive.google.com/file/d/1CnPH3Ct_YF8H7bLHx0X84TMf3mu_HKBz/view?usp=share_link>`_, `VIDEO <https://youtu.be/YxOm6aiDy3E>`_"
     "HPCSYSPRO_ workshop at SC22_", "`An Automated Approach to Continuous Acceptance Testing of HPC Systems at NERSC <https://sc22.supercomputing.org/session/?sess=sess463>`__", "Nov 14, 2022", "`PDF <https://drive.google.com/file/d/1BLJ0pfMBc3ZTitVIA6VGRDAZdI4gBBul/view?usp=share_link>`__, `PPTX <https://docs.google.com/presentation/d/12hwHxONA_CEBiX3JB3jkqarkVpZnkZTOWsnmhNmGgog/edit?usp=share_link>`__"
     "`PEARC22 <https://pearc.acm.org/pearc22/>`_", "**Testing your HPC System with Buildtest**", "July 11, 2022", "`PDF <https://drive.google.com/file/d/1bbbwCH62M7TSAE-lWecgt7R8myeHYhDV/view?usp=sharing>`__, `PPTX <https://docs.google.com/presentation/d/1nwk7dUhX7qEV75bE_P6lrR5Ion9M8AcG/edit?usp=sharing&ouid=102126664227037583807&rtpof=true&sd=true>`__"
     "`ECP Training Event <https://www.exascaleproject.org/training-events/>`_", "`Facility Testing of E4S via E4S Testsuite, Spack Test, and buildtest <https://www.exascaleproject.org/event/buildtest-21-09/>`_", "Sep 14, 2021", "`PDF <https://drive.google.com/file/d/1nfHm7Y3CXkMgNlMKYtVbwIciD637qhRL/view?usp=sharing>`__, `PPTX <https://docs.google.com/presentation/d/1DeguAqcSE8kZy2Hkzr_HNEssFEwaKXdt/edit?usp=sharing&ouid=115649024792605360450&rtpof=true&sd=true>`__"
diff --git a/docs/contributing/code_contribution_guide.rst b/docs/contributing/code_contribution_guide.rst
index 91bb35faf..125e73c90 100644
--- a/docs/contributing/code_contribution_guide.rst
+++ b/docs/contributing/code_contribution_guide.rst
@@ -281,6 +281,83 @@ buildtest source code and regression test. You can do that by running::
 
     pyflakes buildtest tests
 
+Running yamllint
+------------------
+
+We are using `yamllint <https://yamllint.readthedocs.io/en/stable/>`_, which is a linter for YAML files. This package can
+be installed when installing the development dependencies (``pip install -r docs/requirements.txt``). We have a
+configuration file `.yamllint.yml <https://github.com/buildtesters/buildtest/blob/devel/.yamllint.yml>`_ used for configuring
+yamllint.
+
+You can run `yamllint` against any file or and it will show the lint errors such as this example below
+
+.. code-block:: console
+
+     yamllint .github/workflows/style.yml
+    .github/workflows/style.yml
+      18:81     warning  line too long (103 > 80 characters)  (line-length)
+      36:81     warning  line too long (107 > 80 characters)  (line-length)
+
+You **don't** need to specify path to configuration file (i.e ``yamllint -c /path/to/.yamllint.yml``) because **.yamllint.yml** is a default
+configuration file by the linter.
+Please refer to https://yamllint.readthedocs.io/en/stable/configuration.html for more details on configuration options for the linter.
+
+The `Style Check <https://github.com/buildtesters/buildtest/blob/devel/.github/workflows/style.yml>`_ workflow is responsible for running the
+`yamllinter` check on the buildtest codebase. Please refer to the CI check, when debugging linter errors.
+
+Shell Check
+------------
+
+We are using `shellcheck <https://github.com/koalaman/shellcheck>`_ a static analysis tool for checking shell scripts. This package can be installed
+in your system using package manager of your choice. Please refer to `README <https://github.com/koalaman/shellcheck#readme>`_ for more details on
+installation.
+
+The `shellcheck` binary can be used to check `bash` or `sh` scripts. A typical output will consist of list of error codes with line number where error
+appears such as one below
+
+.. code-block:: console
+
+      shellcheck bin/buildtest
+
+    In bin/buildtest line 14:
+            export BUILDTEST_PYTHON="$(command -v "$cmd")"
+                   ^--------------^ SC2155 (warning): Declare and assign separately to avoid masking return values.
+
+
+    In bin/buildtest line 21:
+    ":"""
+    ^---^ SC2317 (info): Command appears to be unreachable. Check usage (or ignore if invoked indirectly).
+
+
+    In bin/buildtest line 23:
+    import os
+    ^-------^ SC2317 (info): Command appears to be unreachable. Check usage (or ignore if invoked indirectly).
+
+
+    In bin/buildtest line 24:
+    import sys
+    ^--------^ SC2317 (info): Command appears to be unreachable. Check usage (or ignore if invoked indirectly).
+
+
+    In bin/buildtest line 26:
+    buildtest_file=os.path.realpath(os.path.expanduser(__file__))
+    ^------------^ SC2034 (warning): buildtest_file appears unused. Verify use (or export if used externally).
+    ^-----------------------------^ SC2317 (info): Command appears to be unreachable. Check usage (or ignore if invoked indirectly).
+                                   ^-- SC1036 (error): '(' is invalid here. Did you forget to escape it?
+                                   ^-- SC1088 (error): Parsing stopped here. Invalid use of parentheses?
+
+    For more information:
+      https://www.shellcheck.net/wiki/SC2034 -- buildtest_file appears unused. Ve...
+      https://www.shellcheck.net/wiki/SC2155 -- Declare and assign separately to ...
+      https://www.shellcheck.net/wiki/SC2317 -- Command appears to be unreachable...
+
+We have configured `shellcheck` with a configuration file `.shellcheckrc <https://github.com/buildtesters/buildtest/blob/devel/.shellcheckrc>`_ that
+can be used to disable certain error codes from checks. This is equivalent to running `shellcheck -e <CODE1>,<CODE2>` but we included this in configuration
+file to make it the default setting.
+
+We have a shellcheck workflow https://github.com/buildtesters/buildtest/blob/devel/.github/workflows/shellcheck.yml that will perform check on shell
+scripts, please refer to the CI results when troubleshooting errors.
+
 Running stylechecks via ``buildtest stylecheck``
 ---------------------------------------------------
 
diff --git a/docs/contributing/new_maintainer_checklist.rst b/docs/contributing/new_maintainer_checklist.rst
index 9cc4eff92..13f6c5162 100644
--- a/docs/contributing/new_maintainer_checklist.rst
+++ b/docs/contributing/new_maintainer_checklist.rst
@@ -26,12 +26,12 @@ started send an invitation email as follows::
     channel.
 
     If you agree to these terms, you will be assigned to work with another buildtest
-    maintainer in your first two weeks. Once you are confident in your duties, we
-    will let you work independently at your own pace, should you need help please
-    contact one of the buildtesters.
+    maintainer in your first two weeks. Once you are confident in your duties, you
+    will work independently at your own pace, should you need assistance please
+    contact one of the maintainers.
 
     Please review the contributing guide: https://buildtest.readthedocs.io/en/devel/contributing.html
-    if you are unsure about your responsibilities as a buildtesters.
+    if you are unsure about your responsibilities as a maintainer.
 
     If you agree to these terms and conditions, please reply "I CONFIRM".
 
@@ -41,6 +41,9 @@ started send an invitation email as follows::
 Onboarding Checklist
 ---------------------
 
+The on-boarding checklist is used to transition an existing contributor to maintainer roler. A maintainer should exercise
+judgement, when on-boarding a contributor to maintainer role which has elevated priviledge to buildtest operations.
+
  - Please make sure the maintainer has a GitHub account if not please create an account at https://github.com/join.
 
  - Ensure user has setup `two-factor authentication (2FA) <https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/securing-your-account-with-two-factor-authentication-2fa>`_ with GitHub.
@@ -53,4 +56,32 @@ Onboarding Checklist
 
  - Once member is added to Slack, ensure member has the appropriate account type. Generally you will want member to be a **Workspace Admin** for more details see `Slack Roles & Permissions <https://slack.com/help/categories/360000049043-Getting-started#understand-roles-permissions>`_.
 
- - Ensure member has an account at ReadTheDocs if not please request member to create an account at https://readthedocs.org/accounts/signup/. Once member has an account please add member to buildtest readthedocs project at https://readthedocs.org/dashboard/buildtest/users/. This will ensure user has ability to access readthedocs platform when troubleshooting build errors related to documentation.
\ No newline at end of file
+ - Ensure member has an account at ReadTheDocs if not please request member to create an account at https://readthedocs.org/accounts/signup/. Once member has an account please add member to buildtest readthedocs project at https://readthedocs.org/dashboard/buildtest/users/. This will ensure user has ability to access readthedocs platform when troubleshooting build errors related to documentation.
+
+  - Please grant user access to NERSC System, if he/she does not have an account, please have them fill out a form and apply for project **m3503**. For more detail see https://docs.nersc.gov/accounts/.
+
+  - We have a buildtest mirror at https://software.nersc.gov/NERSC/buildtest, you should consider giving maintainer elevated privilege such as `Maintainer` role. Please go to https://software.nersc.gov/NERSC/buildtest/-/project_members and add member to group with the desired role. Note `maintainer` access is only required to see project settings, for more operations one can get by with `developer` permission which allows one to see CI job results.
+
+  - We have a mirror of buildtest at `Oak Ridge Leadership Computing Facility (OLCF) <https://docs.olcf.ornl.gov/>`_ system. Please have the individual apply for **new** OLCF account, for more details see https://docs.olcf.ornl.gov/accounts/index.html. You should contact `Shahzeb Siddiqui <mailto:shahzebsiddiqui@lbl.gov>`_ in-order for you to be added to OLCF project account. Once you have an account, please make sure you have access to project https://code.ornl.gov/ecpcitest/buildtest which is the mirror of buildtest. Please make sure the individual has `maintainer` role.
+
+Offboarding Checklist
+----------------------
+
+The offboarding checklist is meant to remove privileges for maintainers that decide to leave the project or
+transition to new role where certain permissions are no longer needed. First confirm with individual that
+he/she would like to be removed as maintainer. Often times, the individual may continue contribution to
+buildtest but doesn't need elevated privilege.
+
+To get started please consider the following when off-boarding maintainers
+
+- Check buildtesters organization https://github.com/orgs/buildtesters/people and update the membership for the individual
+
+- Check buildtest settings https://github.com/buildtesters/buildtest/settings/access to determine appropriate role for member. If individual has a `maintainer` role, please update this to a lower permission scope.
+
+- Remove `maintainers` role from buildtest project in ReadTheDocs https://readthedocs.org/dashboard/buildtest/users/
+
+- Please reassign any `open issues <https://github.com/buildtesters/buildtest/issues>`_ for the maintainer, unless he/she decides to work on the task.
+
+- Follow up with any open `Pull Requests <https://github.com/buildtesters/buildtest/pulls>`_, if you can get maintainer to finish task that would be great, otherwise please reassign pull request to another maintainer or close task.
+
+- Check if maintainer has access to Google Analytics for buildtest documentation, if so, please remove user email from Google Analytics.
\ No newline at end of file
diff --git a/docs/gettingstarted/buildspecs_interface.rst b/docs/gettingstarted/buildspecs_interface.rst
index 0ee8da59f..68928ae04 100644
--- a/docs/gettingstarted/buildspecs_interface.rst
+++ b/docs/gettingstarted/buildspecs_interface.rst
@@ -53,6 +53,20 @@ let's limit output to 5 entries, we can run the following
 
     .. command-output:: buildtest buildspec find --count=5
 
+You can use the ``--row-count`` option to retrieve total number of records from the ``buildtest buildspec find`` query. In the next example, we show retrieved number of 
+tests available in the buildspec cache by running the following.
+
+.. dropdown:: ``buildtest buildspec find --row-count``
+
+    .. command-output:: buildtest buildspec find --row-count
+
+You may find it useful to use the ``--row-count`` feature while filtering the buildspec cache. For instance, you can get the number of buildspecs in the cache with
+the script schema type, this can be done by running ``buildtest bc find --filter type=script --row-count`` as shown below.
+
+.. dropdown:: ``buildtest buildspec find --filter type=script --row-count``
+
+    .. command-output:: buildtest buildspec find --filter type=script --row-count
+
 Finding buildspec files
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/docs/index.rst b/docs/index.rst
index 3dabb43fa..1d580faed 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -11,9 +11,9 @@ If you are working off the `devel <https://github.com/buildtesters/buildtest/tre
 https://buildtest.readthedocs.io/en/devel/
 which references the `devel` branch.
 
-.. |docs| image:: https://readthedocs.org/projects/buildtest/badge/?version=latest
+.. |docs| image:: https://readthedocs.org/projects/buildtest/badge/?version=devel
     :alt: Documentation Status
-    :target: https://buildtest.readthedocs.io/en/latest/?badge=latest
+    :target: https://readthedocs.org/projects/buildtest/builds/
 
 .. |license| image:: https://img.shields.io/github/license/buildtesters/buildtest.svg
 
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 75aea7721..3f0362c4d 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -10,4 +10,5 @@ sphinx-rtd-theme
 sphinxcontrib-programoutput
 sphinxext-remoteliteralinclude
 sphinx-copybutton
-readthedocs-sphinx-search
\ No newline at end of file
+readthedocs-sphinx-search
+yamllint
diff --git a/perlmutter_tutorial/ex3/.solution.sh b/perlmutter_tutorial/ex3/.solution.sh
new file mode 100755
index 000000000..96a2999d3
--- /dev/null
+++ b/perlmutter_tutorial/ex3/.solution.sh
@@ -0,0 +1,21 @@
+#1
+buildtest buildspec find --tags
+
+#2
+buildtest buildspec find --helpfilter
+buildtest buildspec find --helpformat
+
+#3
+buildtest buildspec find --format name,description
+
+#4
+buildtest buildspec find --filter tags=e4s
+
+#5
+buildtest buildspec find invalid
+
+#6 
+buildtest buildspec validate -t e4s
+
+#7
+buildtest buildspec show hello_world_openmp
diff --git a/perlmutter_tutorial/ex3/.solution.txt b/perlmutter_tutorial/ex3/.solution.txt
deleted file mode 100644
index 6cb72404a..000000000
--- a/perlmutter_tutorial/ex3/.solution.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-1. buildtest buildspec find --tags
-2. buildtest buildspec find --helpfilter
-2. buildtest buildspec find --helpformat
-3. buildtest buildspec find --format name,description
-4. buildtest buildspec find --filter tags=e4s
-5. buildtest buildspec find invalid
-6. buildtest buildspec validate -t e4s
-7. buildtest buildspec show hello_world_openmp
\ No newline at end of file
diff --git a/perlmutter_tutorial/ex4/.solution.sh b/perlmutter_tutorial/ex4/.solution.sh
new file mode 100755
index 000000000..aff7e1280
--- /dev/null
+++ b/perlmutter_tutorial/ex4/.solution.sh
@@ -0,0 +1,12 @@
+#1
+buildtest report --helpfilter
+buildtest report --helpformat
+
+#2
+buildtest report --filter returncode=0
+
+#3
+buildtest report --filter tags=e4s
+
+#4
+buildtest report --fail --row-count
diff --git a/perlmutter_tutorial/ex4/.solution.txt b/perlmutter_tutorial/ex4/.solution.txt
deleted file mode 100644
index c0525c887..000000000
--- a/perlmutter_tutorial/ex4/.solution.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-1. buildtest report --helpfilter
-1. buildtest report --helpformat
-2. buildtest report --filter returncode=0
-3. buildtest report --filter --tags=e4s
-4. buildtest report --fail --row-count
diff --git a/requirements.txt b/requirements.txt
index 9cfe74d5f..5ebc641a3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,5 +8,5 @@ rich
 coverage
 pytest
 isort
-black==22.12.0
+black==23.1.0
 pyflakes
\ No newline at end of file
diff --git a/scripts/spack_container/install.sh b/scripts/spack_container/install.sh
index 912c16cef..11c156afa 100644
--- a/scripts/spack_container/install.sh
+++ b/scripts/spack_container/install.sh
@@ -3,12 +3,12 @@
 ## For more details see spack tutorial: https://spack-tutorial.readthedocs.io/en/latest/
 
 git clone https://github.com/spack/spack ~/spack
-cd ~/spack
+cd ~/spack || { echo "Unable to find spack directory"; exit 1;}
 git checkout releases/v0.16
 . share/spack/setup-env.sh
 spack tutorial -y
 spack install lmod
 spack install python
 
-cp $HOME/buildtest/scripts/spack_container/modules.yaml $SPACK_ROOT/etc/spack/
+cp "${HOME}/buildtest/scripts/spack_container/modules.yaml" "${SPACK_ROOT}/etc/spack/"
 spack module tcl refresh -y --delete-tree
\ No newline at end of file
diff --git a/scripts/spack_container/setup.sh b/scripts/spack_container/setup.sh
index ab180cac8..4d392f61c 100644
--- a/scripts/spack_container/setup.sh
+++ b/scripts/spack_container/setup.sh
@@ -6,10 +6,10 @@
 module load python
 
 # setup python environment
-python -m venv ~/pyenv/buildtest
-source ~/pyenv/buildtest/bin/activate
+python -m venv "$HOME/pyenv/buildtest"
+source "$HOME/pyenv/buildtest/bin/activate"
 
-cd ~/buildtest
+cd ~/buildtest || { echo "Unable to cd to 'buildtest' directory"; exit 1;}
 # installing buildtest
 source setup.sh
 
diff --git a/scripts/spack_container/spack_setup.sh b/scripts/spack_container/spack_setup.sh
index 1176eba01..f378884c9 100644
--- a/scripts/spack_container/spack_setup.sh
+++ b/scripts/spack_container/spack_setup.sh
@@ -1,4 +1,4 @@
 #!/bin/bash
 . /home/spack/spack/share/spack/setup-env.sh
-. $(spack location -i lmod)/lmod/lmod/init/bash
+. "$(spack location -i lmod)/lmod/lmod/init/bash"
 #spack module tcl refresh --delete-tree -y
diff --git a/setup.csh b/setup.csh
index cafc27eca..b6e8d3759 100755
--- a/setup.csh
+++ b/setup.csh
@@ -1,7 +1,7 @@
 #!/bin/csh
 # MIT License
 
-# Copyright (c) 2021-2022, The Regents of the University of California,
+# Copyright (c) 2021-2023, The Regents of the University of California,
 # through Lawrence Berkeley National Laboratory (subject to receipt of
 # any required approvals from the U.S. Dept. of Energy), Shahzeb Siddiqui,
 # and Vanessa Sochat. All rights reserved.
diff --git a/setup.sh b/setup.sh
index 9c8ba8bb7..99256124b 100755
--- a/setup.sh
+++ b/setup.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 # MIT License
 
-# Copyright (c) 2021-2022, The Regents of the University of California,
+# Copyright (c) 2021-2023, The Regents of the University of California,
 # through Lawrence Berkeley National Laboratory (subject to receipt of
 # any required approvals from the U.S. Dept. of Energy), Shahzeb Siddiqui,
 # and Vanessa Sochat. All rights reserved.
@@ -44,10 +44,11 @@ returncode=$?
 # if we are unable to import buildtest.main then install buildtest dependencies
 if [ $returncode -ne 0 ]; then
   #$pip install --target ${buildtest_root}/.packages -r ${buildtest_root}/requirements.txt &> /dev/null
-  $pip install -r ${buildtest_root}/requirements.txt &> /dev/null
+  $pip install -r "${buildtest_root}/requirements.txt" &> /dev/null
 fi
 
 export BUILDTEST_ROOT=$buildtest_root
+export COVERAGE_PROCESS_START=$buildtest_root/.coveragerc
 export PATH=${buildtest_root}/bin:$PATH
 
 # for ZSH shell need to run autoload see https://stackoverflow.com/questions/3249432/can-a-bash-tab-completion-script-be-used-in-zsh
@@ -58,7 +59,7 @@ export PATH=${buildtest_root}/bin:$PATH
 #fi
 
 # enable bash completion script
-source $buildtest_root/bash_completion.sh
+source "$buildtest_root/bash_completion.sh"
 
 # allow buildtest source code to PYTHONPATH so python can import buildtest
 if [ -z "$PYTHONPATH" ]; then
diff --git a/tutorials/perf_checks/assert_eq.yml b/tutorials/perf_checks/assert_eq.yml
index 2eeccd5dc..0fcfda19b 100644
--- a/tutorials/perf_checks/assert_eq.yml
+++ b/tutorials/perf_checks/assert_eq.yml
@@ -46,4 +46,4 @@ buildspecs:
         - name: first
           ref: John
         - name: last
-          ref: Smith
\ No newline at end of file
+          ref: Smith
diff --git a/tutorials/perf_checks/assert_ge.yml b/tutorials/perf_checks/assert_ge.yml
index cabd35e30..5c79d185e 100644
--- a/tutorials/perf_checks/assert_ge.yml
+++ b/tutorials/perf_checks/assert_ge.yml
@@ -43,4 +43,4 @@ buildspecs:
         - name: add
           ref: 6000
         - name: triad
-          ref: 6500
\ No newline at end of file
+          ref: 6500
diff --git a/tutorials/perf_checks/assert_gt.yml b/tutorials/perf_checks/assert_gt.yml
index 0fe7c357e..882f1ade7 100644
--- a/tutorials/perf_checks/assert_gt.yml
+++ b/tutorials/perf_checks/assert_gt.yml
@@ -43,4 +43,4 @@ buildspecs:
         - name: add
           ref: 6000
         - name: triad
-          ref: 6500
\ No newline at end of file
+          ref: 6500
diff --git a/tutorials/perf_checks/assert_le.yml b/tutorials/perf_checks/assert_le.yml
index 6a47af8ea..148b64837 100644
--- a/tutorials/perf_checks/assert_le.yml
+++ b/tutorials/perf_checks/assert_le.yml
@@ -43,4 +43,4 @@ buildspecs:
         - name: add
           ref: 6000
         - name: triad
-          ref: 6500
\ No newline at end of file
+          ref: 6500
diff --git a/tutorials/perf_checks/assert_lt.yml b/tutorials/perf_checks/assert_lt.yml
new file mode 100644
index 000000000..1a2febcdc
--- /dev/null
+++ b/tutorials/perf_checks/assert_lt.yml
@@ -0,0 +1,46 @@
+buildspecs:
+  assert_lt_example:
+    type: script
+    executor: generic.local.bash
+    description: Run stream test with metrics example using assert less than
+    env:
+      OMP_NUM_THREADS: 4
+    run: |
+      wget https://raw.githubusercontent.com/jeffhammond/STREAM/master/stream.c
+      gcc -openmp -o stream stream.c
+      ./stream
+    metrics:
+      copy:
+        type: float
+        regex:
+          exp: 'Copy:\s+(\S+)\s+.*'
+          stream: stdout
+          item: 1
+      scale:
+        type: float
+        regex:
+          exp: 'Scale:\s+(\S+)\s+.*'
+          stream: stdout
+          item: 1
+      add:
+        type: float
+        regex:
+          exp: 'Add:\s+(\S+)\s+.*'
+          stream: stdout
+          item: 1
+      triad:
+        type: float
+        regex:
+          exp: 'Triad:\s+(\S+)\s+.*'
+          stream: stdout
+          item: 1
+    status:
+      assert_lt:
+        - name: copy
+          ref: 5000
+        - name: scale
+          ref: 5500
+        - name: add
+          ref: 6000
+        - name: triad
+          ref: 6500
diff --git a/tutorials/perf_checks/assert_ne.yml b/tutorials/perf_checks/assert_ne.yml
index 0236dcd77..dc4316881 100644
--- a/tutorials/perf_checks/assert_ne.yml
+++ b/tutorials/perf_checks/assert_ne.yml
@@ -46,4 +46,4 @@ buildspecs:
         - name: first
           ref: Robert
         - name: last
-          ref: Brown
\ No newline at end of file
+          ref: Brown
diff --git a/tutorials/perf_checks/assert_range.yml b/tutorials/perf_checks/assert_range.yml
index 9e20fccfc..acbe949a2 100644
--- a/tutorials/perf_checks/assert_range.yml
+++ b/tutorials/perf_checks/assert_range.yml
@@ -47,4 +47,4 @@ buildspecs:
           upper: 20000
         - name: triad
           lower: 5600
-          upper: 20000
\ No newline at end of file
+          upper: 20000
diff --git a/tutorials/perf_checks/contains.yml b/tutorials/perf_checks/contains.yml
index 73181cf82..958e6eee9 100644
--- a/tutorials/perf_checks/contains.yml
+++ b/tutorials/perf_checks/contains.yml
@@ -39,4 +39,4 @@ buildspecs:
     status:
       contains:
         - name: x
-          ref: [ '1', 2, 4, 8 ]
\ No newline at end of file
+          ref: ['1', 2, 4, 8]


Test patch:
diff --git a/bin/buildtest b/bin/buildtest
index f5f45ef91..acda17c17 100755
--- a/bin/buildtest
+++ b/bin/buildtest
@@ -23,8 +23,8 @@ exit 1
 import os
 import sys
 
-buildtest_file = os.path.realpath(os.path.expanduser(__file__))
-prefix = os.path.dirname(os.path.dirname(buildtest_file))
+buildtest_file=os.path.realpath(os.path.expanduser(__file__))
+prefix=os.path.dirname(os.path.dirname(buildtest_file))
 
 sys.path.insert(0, prefix)
 
diff --git a/buildtest/__init__.py b/buildtest/__init__.py
index 7cb831513..caca2e30c 100644
--- a/buildtest/__init__.py
+++ b/buildtest/__init__.py
@@ -1,3 +1,3 @@
-BUILDTEST_VERSION = "1.2"
+BUILDTEST_VERSION = "1.3"
 __version__ = BUILDTEST_VERSION
 BUILDTEST_COPYRIGHT = "Copyright (c) 2021-2023, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy), Shahzeb Siddiqui, and Vanessa Sochat. All rights reserved."
diff --git a/buildtest/builders/base.py b/buildtest/builders/base.py
index b27127740..024ea3847 100644
--- a/buildtest/builders/base.py
+++ b/buildtest/builders/base.py
@@ -22,12 +22,16 @@
     assert_ge_check,
     assert_gt_check,
     assert_le_check,
+    assert_lt_check,
     assert_ne_check,
     assert_range_check,
     contains_check,
     exists_check,
+    file_count_check,
+    file_regex_check,
     is_dir_check,
     is_file_check,
+    is_symlink_check,
     notcontains_check,
     regex_check,
     returncode_check,
@@ -545,7 +549,6 @@ def _write_build_script(self, modules=None, modulepurge=None, unload_modules=Non
         if modules:
             lines.append("# Specify list of modules to load")
             for module in modules.split(","):
-
                 lines.append(f"module load {module}")
 
         lines += [
@@ -715,13 +718,11 @@ def get_job_directives(self):
             lines += [f"#BSUB -e {self.name}.err"]
 
         if self.pbs:
-
             for line in self.pbs:
                 lines.append(f"#PBS {line}")
             lines.append(f"#PBS -N {self.name}")
 
         if self.cobalt:
-
             for line in self.cobalt:
                 lines.append(f"#COBALT {line}")
             lines.append(f"#COBALT --jobname={self.name}")
@@ -867,13 +868,11 @@ def add_metrics(self):
             return
 
         for key in self.metrics.keys():
-
             # default value of metric is empty string
             self.metadata["metrics"][key] = ""
 
             # apply regex on stdout/stderr and assign value to metrics
             if self.metrics[key].get("regex"):
-
                 if self.metrics[key]["regex"]["stream"] == "stdout":
                     content = self._output
                 elif self.metrics[key]["regex"]["stream"] == "stderr":
@@ -961,21 +960,24 @@ def check_test_state(self):
 
         # if status is defined in Buildspec, then check for returncode and regex
         if self.status:
-
             slurm_job_state_match = False
             pbs_job_state_match = False
             lsf_job_state_match = False
             assert_ge_match = False
             assert_le_match = False
             assert_gt_match = False
+            assert_lt_match = False
             assert_eq_match = False
             assert_ne_match = False
             assert_range_match = False
             assert_contains_match = False
             assert_notcontains_match = False
+            assert_is_symlink = False
             assert_exists = False
             assert_is_dir = False
             assert_is_file = False
+            file_regex_match = False
+            assert_file_count = False
 
             # returncode_match is boolean to check if reference returncode matches return code from test
             returncode_match = returncode_check(self)
@@ -990,6 +992,9 @@ def check_test_state(self):
             self.metadata["check"]["runtime"] = runtime_match
             self.metadata["check"]["returncode"] = returncode_match
 
+            if self.status.get("file_regex"):
+                file_regex_match = file_regex_check(self)
+
             if self.status.get("slurm_job_state") and isinstance(self.job, SlurmJob):
                 slurm_job_state_match = (
                     self.status["slurm_job_state"] == self.job.state()
@@ -1010,6 +1015,9 @@ def check_test_state(self):
             if self.status.get("assert_gt"):
                 assert_gt_match = assert_gt_check(self)
 
+            if self.status.get("assert_lt"):
+                assert_lt_match = assert_lt_check(self)
+
             if self.status.get("assert_eq"):
                 assert_eq_match = assert_eq_check(self)
 
@@ -1025,6 +1033,9 @@ def check_test_state(self):
             if self.status.get("not_contains"):
                 assert_notcontains_match = notcontains_check(self)
 
+            if self.status.get("is_symlink"):
+                assert_is_symlink = is_symlink_check(builder=self)
+
             if self.status.get("exists"):
                 assert_exists = exists_check(builder=self)
 
@@ -1034,11 +1045,15 @@ def check_test_state(self):
             if self.status.get("is_file"):
                 assert_is_file = is_file_check(builder=self)
 
+            if self.status.get("file_count"):
+                assert_file_count = file_count_check(builder=self)
+
             # if any of checks is True we set the 'state' to PASS
             state = any(
                 [
                     returncode_match,
                     regex_match,
+                    file_regex_match,
                     slurm_job_state_match,
                     pbs_job_state_match,
                     lsf_job_state_match,
@@ -1046,14 +1061,17 @@ def check_test_state(self):
                     assert_ge_match,
                     assert_le_match,
                     assert_gt_match,
+                    assert_lt_match,
                     assert_eq_match,
                     assert_ne_match,
                     assert_range_match,
                     assert_contains_match,
                     assert_notcontains_match,
+                    assert_is_symlink,
                     assert_exists,
                     assert_is_dir,
                     assert_is_file,
+                    assert_file_count,
                 ]
             )
             if state:
diff --git a/buildtest/builders/compiler.py b/buildtest/builders/compiler.py
index d7bc36045..7697896cc 100644
--- a/buildtest/builders/compiler.py
+++ b/buildtest/builders/compiler.py
@@ -429,7 +429,6 @@ def _process_compiler_config(self):
 
         # if default compiler setting provided in buildspec let's assign it.
         if deep_get(self.compiler_section, "default", self.compiler_group):
-
             self.cc = (
                 self.compiler_section["default"][self.compiler_group].get("cc")
                 or self.cc
@@ -462,7 +461,6 @@ def _process_compiler_config(self):
             )
         # if compiler instance defined in config section read from buildspec. This overrides default section if specified
         if deep_get(self.compiler_section, "config", self.compiler):
-
             self.logger.debug(
                 f"[{self.name}]: Detected compiler: {self.compiler} in 'config' scope overriding default compiler group setting for: {self.compiler_group}"
             )
diff --git a/buildtest/builders/script.py b/buildtest/builders/script.py
index 3a911058c..277887a01 100644
--- a/buildtest/builders/script.py
+++ b/buildtest/builders/script.py
@@ -26,7 +26,6 @@ def __init__(
         numnodes=None,
         compiler=None,
     ):
-
         super().__init__(
             name=name,
             recipe=recipe,
@@ -123,7 +122,6 @@ def generate_script(self):
             lines += sched_lines
 
         if self.burstbuffer:
-
             burst_buffer_lines = self._get_burst_buffer(self.burstbuffer)
             if burst_buffer_lines:
                 lines += burst_buffer_lines
diff --git a/buildtest/builders/spack.py b/buildtest/builders/spack.py
index fd8368c9f..6d86b6f33 100644
--- a/buildtest/builders/spack.py
+++ b/buildtest/builders/spack.py
@@ -211,13 +211,11 @@ def _spack_environment(self, spack_env):
             lines.append(f"spack env rm -y {spack_env['rm']['name']}")
 
         if spack_env.get("create"):
-
             opts = spack_env["create"].get("options") or ""
             cmd = ["spack env create", opts]
 
             # create spack environment from name
             if spack_env["create"].get("name"):
-
                 # if remove_environment is defined we remove the environment before creating it
                 if spack_env["create"].get("remove_environment"):
                     lines.append(f"spack env rm -y {spack_env['create']['name']}")
@@ -246,7 +244,6 @@ def _spack_environment(self, spack_env):
 
             # activate spack environment via directory 'spack env activate -d <dir>'
             elif spack_env["activate"].get("dir"):
-
                 env_dir = resolve_path(spack_env["activate"]["dir"], exist=False)
                 if not env_dir:
                     raise BuildTestError(
diff --git a/buildtest/buildsystem/builders.py b/buildtest/buildsystem/builders.py
index 615ca7612..712bef7c4 100644
--- a/buildtest/buildsystem/builders.py
+++ b/buildtest/buildsystem/builders.py
@@ -78,7 +78,6 @@ def __init__(
             return
 
         if deep_get(self.filters, "maintainers"):
-
             if not self.bp.recipe.get("maintainers"):
                 console.print(
                     f"{self.bp.buildspec}: skipping test because [italic]'maintainers'[/italic] field is not specified in buildspec."
@@ -112,7 +111,6 @@ def __init__(
                 # Add the builder for the script or spack schema
 
                 if recipe["type"] in ["script", "compiler", "spack"]:
-
                     builders = self.build(name, recipe)
                     if builders:
                         self.builders += builders
@@ -202,7 +200,6 @@ def create_script_builders(
     def create_compiler_builders(
         self, name, recipe, executor, nodes=None, procs=None, compiler_name=None
     ):
-
         """Create builder objects by calling :class:`buildtest.builders.compiler.CompilerBuilder` class.
 
         args:
@@ -403,7 +400,6 @@ def build(self, name, recipe):
         for compiler_pattern in recipe["compilers"]["name"]:
             for bc_name in discovered_compilers:
                 if re.match(compiler_pattern, bc_name):
-
                     builder = self.generate_builders(
                         name=name, recipe=recipe, compiler_name=bc_name
                     )
@@ -463,7 +459,6 @@ def _skip_tests_by_type(self, recipe, name):
         """
 
         if self.filters.get("type"):
-
             found = self.filters["type"] == recipe["type"]
 
             if not found:
diff --git a/buildtest/buildsystem/checks.py b/buildtest/buildsystem/checks.py
index 8886b037a..aa049846a 100644
--- a/buildtest/buildsystem/checks.py
+++ b/buildtest/buildsystem/checks.py
@@ -2,7 +2,15 @@
 import re
 
 from buildtest.defaults import console
-from buildtest.utils.file import is_dir, is_file, resolve_path
+from buildtest.utils.file import (
+    is_dir,
+    is_file,
+    is_symlink,
+    read_file,
+    resolve_path,
+    search_files,
+    walk_tree,
+)
 
 logger = logging.getLogger(__name__)
 
@@ -35,7 +43,6 @@ def returncode_check(builder):
 
     # if 'returncode' field set for 'status' check the returncode if its not set we return False
     if "returncode" in builder.status.keys():
-
         # returncode can be an integer or list of integers
         buildspec_returncode = builder.status["returncode"]
 
@@ -99,6 +106,60 @@ def runtime_check(builder):
     return float(min_time) < actual_runtime < float(max_time)
 
 
+def file_regex_check(builder):
+    """This method will check if file exists and conduct a regular expression check using
+    `re.search <https://docs.python.org/3/library/re.html#re.search>`_ method. This method is invoked if ``file_regex`` is defined in ``status`` field.
+    If file doesn't exist we return False. If file exists we read the file and apply regular expression for every file specified in ``file_regex`` field.
+
+    Args:
+        builder (buildtest.builders.base.BuilderBase): An instance of BuilderBase class used for printing the builder name
+
+    Returns:
+        bool: Returns True if there is a regex match otherwise returns False.
+    """
+
+    assert_file_regex = []
+
+    for file_check in builder.status["file_regex"]:
+        fname = file_check["file"]
+        resolved_fname = resolve_path(fname)
+        if not resolved_fname:
+            msg = f"[blue]{builder}[/]: Unable to resolve file path: {fname}"
+            logger.error(msg)
+            console.print(msg, style="red")
+            assert_file_regex.append(False)
+            continue
+
+        if not is_file(resolved_fname):
+            msg = f"[blue]{builder}[/]: File: {resolved_fname} is not a file"
+            logger.error(msg)
+            console.print(msg, style="red")
+            assert_file_regex.append(False)
+            continue
+
+        # read file and apply regex
+        content = read_file(resolved_fname)
+        regex = re.search(file_check["exp"], content)
+        console.print(
+            f"[blue]{builder}[/]: Performing regex expression '{file_check['exp']}' on file {resolved_fname}"
+        )
+
+        if not regex:
+            msg = f"[blue]{builder}[/]: Regular expression: '{file_check['exp']}' is not found in file: {resolved_fname}"
+            logger.error(msg)
+            console.print(msg, style="red")
+            assert_file_regex.append(False)
+            continue
+
+        assert_file_regex.append(True)
+
+        console.print(
+            f"[blue]{builder}[/]: [green]Regular expression on file {resolved_fname} is a MATCH![/green]"
+        )
+
+    return all(assert_file_regex)
+
+
 def regex_check(builder):
     """This method conducts a regular expression check using
     `re.search <https://docs.python.org/3/library/re.html#re.search>`_
@@ -152,6 +213,36 @@ def regex_check(builder):
     return True
 
 
+def is_symlink_check(builder):
+    """This method will perform symlink status check for ``is_symlink`` property. Each item is tested for symblolic link
+    and returns a boolean to inform if all items are symbolic links or not.
+
+    Args:
+        builder (buildtest.builders.base.BuilderBase): An instance of BuilderBase class used for printing the builder name
+    Returns:
+        bool: A boolean for is_symlink status check
+    """
+    assert_exists = []
+    console.print(
+        f"[blue]{builder}[/]: Check all items:  {builder.status['is_symlink']}  for symbolic links"
+    )
+    for filename in builder.status["is_symlink"]:
+        if is_symlink(filename):
+            console.print(
+                f"[blue]{builder}[/]: {filename} is a symbolic link to {resolve_path(filename)}"
+            )
+            assert_exists.append(True)
+        else:
+            console.print(
+                f"[blue]{builder}[/]: {filename} is broken or not a symbolic link"
+            )
+            assert_exists.append(False)
+
+    bool_check = all(assert_exists)
+    console.print(f"[blue]{builder}[/]: Symlink Check: {bool_check}")
+    return bool_check
+
+
 def exists_check(builder):
     """This method will perform status check for ``exists`` property. Each value is tested for file
     existence and returns a boolean to inform if all files exist or not.
@@ -478,6 +569,77 @@ def assert_gt_check(builder):
     return bool_check
 
 
+def assert_lt_check(builder):
+    """Perform check on assert less than when ``assert_lt`` is specified in buildspec. The return is a boolean value that determines if the check has passed.
+    One can specify multiple assert checks to check each metric with its reference value. When multiple items are specified, the operation is a logical AND and all checks
+    must be ``True``.
+
+    Args:
+        builder (buildtest.builders.base.BuilderBase): An instance of BuilderBase class used for printing the builder name
+
+    Returns:
+        bool: True or False for performance check ``assert_lt``
+    """
+
+    # a list containing booleans to evaluate reference check for each metric
+    assert_check = []
+
+    metric_names = list(builder.metadata["metrics"].keys())
+
+    # iterate over each metric in buildspec and determine reference check for each metric
+    for metric in builder.status["assert_lt"]:
+        name = metric["name"]
+        ref_value = metric["ref"]
+
+        # if metric is not valid, then mark as False
+        if not builder.is_valid_metric(name):
+            msg = f"[blue]{builder}[/]: Unable to find metric: [red]{name}[/red]. List of valid metrics are the following: {metric_names}"
+            console.print(msg)
+            logger.warning(msg)
+            assert_check.append(False)
+            continue
+
+        metric_value = builder.metadata["metrics"][name]
+
+        if not is_metrics_defined(builder, name):
+            assert_check.append(False)
+            continue
+
+        if builder.metrics[name]["type"] == "str":
+            msg = f"[blue]{builder}[/]: Unable to convert metric: [red]'{name}'[/red] for comparison. The type must be 'int' or 'float' but recieved [red]{builder.metrics[name]['type']}[/red]. "
+            console.print(msg)
+            logger.warning(msg)
+            assert_check.append(False)
+            continue
+
+        # convert metric value and reference value to int
+        conv_value = convert_metrics(
+            metric_value=metric_value,
+            dtype=builder.metrics[name]["type"],
+        )
+        ref_value = convert_metrics(
+            metric_value=ref_value,
+            dtype=builder.metrics[name]["type"],
+        )
+
+        # if there is a type mismatch then let's stop now before we do comparison
+        if (conv_value is None) or (ref_value is None):
+            assert_check.append(False)
+            continue
+
+        bool_check = conv_value < ref_value
+        console.print(
+            f"[blue]{builder}[/]: testing metric: {name} if {conv_value} < {ref_value} - Check: {bool_check}"
+        )
+        assert_check.append(bool_check)
+
+    # perform a logical AND on the list and return the boolean result
+    bool_check = all(assert_check)
+
+    console.print(f"[blue]{builder}[/]: Less Than Check: {bool_check}")
+    return bool_check
+
+
 def assert_eq_check(builder):
     """This method is perform Assert Equality used when ``assert_eq`` property is specified
     in status check. This method will evaluate each metric value reference value and
@@ -807,3 +969,69 @@ def assert_range_check(builder):
 
     console.print(f"[blue]{builder}[/]: Range Check: {range_check}")
     return range_check
+
+
+def file_count_check(builder):
+    """This method is used to perform file count check when ``file_count`` property is specified
+    in status check. This method will evaluate the number of files in a directory and compare it
+    with the reference specified via ``count``. The comparison is done using ``==`` operator.
+
+    Args:
+        builder (buildtest.builders.base.BuilderBase): An instance of BuilderBase class used for printing the builder name
+
+    Returns:
+        bool: True or False for performance check ``file_count``
+    """
+    # a list containing booleans to evaluate reference check for each metric
+    assert_check = []
+
+    # iterate over each metric in buildspec and determine reference check for each metric
+    for dir_check in builder.status["file_count"]:
+        if not is_dir(dir_check["dir"]):
+            msg = f"[blue]{builder}[/]: Unable to find directory: [red]{dir_check['dir']}[/red]."
+            console.print(msg)
+            logger.warning(msg)
+            assert_check.append(False)
+            continue
+
+        files_by_directory_walk = []
+        files_by_regex = []
+
+        # need to walk directory tree if 'ext' attribute is specified or 'filepattern' attribute is not specified.
+        if dir_check.get("ext") or not dir_check.get("filepattern"):
+            files_by_directory_walk = walk_tree(
+                dir_check["dir"],
+                ext=dir_check.get("ext"),
+                max_depth=dir_check.get("depth"),
+                file_type=dir_check.get("filetype"),
+                file_traverse_limit=dir_check.get("file_traverse_limit"),
+            )
+        # if 'filepattern' attribute is specified we will search for files via search_files method which will perform directory traversal based on regular expression
+        if dir_check.get("filepattern"):
+            files_by_regex = search_files(
+                dir_check["dir"],
+                regex_pattern=dir_check["filepattern"],
+                max_depth=dir_check.get("depth"),
+                file_type=dir_check.get("filetype"),
+                file_traverse_limit=dir_check.get("file_traverse_limit"),
+            )
+
+        total_files = list(set(files_by_directory_walk + files_by_regex))
+        bool_check = len(total_files) == dir_check["count"]
+        assert_check.append(bool_check)
+
+        # need to get a resolved path for printing purposes. User can specify arbitrary directory name it may not exist on filesystem
+        resolved_dirname = resolve_path(dir_check["dir"], exist=False)
+        logger.debug(
+            f"[blue]{builder}[/]: Found the following files: {total_files} in directory: {resolved_dirname}"
+        )
+
+        console.print(
+            f"[blue]{builder}[/]: Found {len(total_files)} file in directory: {resolved_dirname}. Comparing with reference count: {dir_check['count']}. Comparison check is {len(total_files)} == {dir_check['count']} which evaluates to {bool_check}"
+        )
+
+    # perform a logical AND on the list and return the boolean result
+    bool_check = all(assert_check)
+
+    console.print(f"[blue]{builder}[/]: File Count Check: {bool_check}")
+    return bool_check
diff --git a/buildtest/buildsystem/parser.py b/buildtest/buildsystem/parser.py
index 7ff3c081f..a2400c507 100644
--- a/buildtest/buildsystem/parser.py
+++ b/buildtest/buildsystem/parser.py
@@ -175,7 +175,6 @@ def validate(self):
         # with sub schema
 
         for test in self.get_test_names():
-
             self.logger.info(
                 "Validating test - '%s' in recipe: %s" % (test, self.buildspec)
             )
diff --git a/buildtest/cli/__init__.py b/buildtest/cli/__init__.py
index 9caa47e3b..f4221bf69 100644
--- a/buildtest/cli/__init__.py
+++ b/buildtest/cli/__init__.py
@@ -150,7 +150,6 @@ def valid_time(value):
 
 
 def get_parser():
-
     epilog_str = f"""
 References
 
@@ -224,15 +223,19 @@ def get_parser():
     )
     parser.add_argument("-r", "--report", help="Specify path to test report file")
 
+    parent_parser = argparse.ArgumentParser(add_help=False)
+    parent_parser.add_argument(
+        "--pager", action="store_true", help="Enable PAGING when viewing result"
+    )
     subparsers = parser.add_subparsers(title="COMMANDS", dest="subcommands", metavar="")
 
     build_menu(subparsers)
-    buildspec_menu(subparsers)
-    config_menu(subparsers)
-    report_menu(subparsers)
-    inspect_menu(subparsers)
+    buildspec_menu(subparsers, parent_parser)
+    config_menu(subparsers, parent_parser)
+    report_menu(subparsers, parent_parser)
+    inspect_menu(subparsers, parent_parser)
     path_menu(subparsers)
-    history_menu(subparsers)
+    history_menu(subparsers, parent_parser)
     schema_menu(subparsers)
     cdash_menu(subparsers)
     unittest_menu(subparsers)
@@ -244,7 +247,11 @@ def get_parser():
 
 
 def misc_menu(subparsers):
-    """Build the command line menu for some miscellaneous commands"""
+    """Build the command line menu for some miscellaneous commands
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object to add subparser
+    """
 
     cd_parser = subparsers.add_parser(
         "cd", help="change directory to root of test given a test name"
@@ -310,7 +317,11 @@ def misc_menu(subparsers):
 
 
 def stylecheck_menu(subparsers):
-    """This method will create command options for ``buildtest stylecheck``"""
+    """This method will create command options for ``buildtest stylecheck``
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object to add subparser
+    """
 
     stylecheck_parser = subparsers.add_parser(
         "stylecheck", aliases=["style"], help="Run buildtest style checks"
@@ -331,7 +342,11 @@ def stylecheck_menu(subparsers):
 
 
 def unittest_menu(subparsers):
-    """This method builds the command line menu for ``buildtest unittests`` command"""
+    """This method builds the command line menu for ``buildtest unittests`` command
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object to add subparser
+    """
 
     unittests_parser = subparsers.add_parser(
         "unittests", help="Run buildtest unit tests", aliases=["test"]
@@ -355,7 +370,11 @@ def unittest_menu(subparsers):
 
 
 def tutorial_examples_menu(subparsers):
-    """This method builds the command line menu for ``buildtest tutorial-examples`` command"""
+    """This method builds the command line menu for ``buildtest tutorial-examples`` command
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object to add subparser
+    """
 
     subparsers.add_parser(
         "tutorial-examples",
@@ -364,7 +383,11 @@ def tutorial_examples_menu(subparsers):
 
 
 def path_menu(subparsers):
-    """This method builds the command line menu for ``buildtest path`` command"""
+    """This method builds the command line menu for ``buildtest path`` command
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object to add subparser
+    """
 
     path = subparsers.add_parser("path", help="Show path attributes for a given test")
     path_group = path.add_mutually_exclusive_group()
@@ -390,8 +413,13 @@ def path_menu(subparsers):
     path.add_argument("name", help="Name of test")
 
 
-def history_menu(subparsers):
-    """This method builds the command line menu for ``buildtest history`` command"""
+def history_menu(subparsers, parent_parser):
+    """This method builds the command line menu for ``buildtest history`` command
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object to add subparser
+        parent_parser (argparse.ArgumentParser): Parent parser object to add to subparser
+    """
 
     history_subcmd = subparsers.add_parser(
         "history", aliases=["hy"], help="Query build history"
@@ -402,7 +430,7 @@ def history_menu(subparsers):
     )
 
     list_parser = history_subparser.add_parser(
-        "list", help="List a summary of all builds"
+        "list", help="List a summary of all builds", parents=[parent_parser]
     )
     list_parser.add_argument(
         "-n",
@@ -416,9 +444,6 @@ def history_menu(subparsers):
         action="store_true",
         help="Print output in machine readable format",
     )
-    list_parser.add_argument(
-        "--pager", action="store_true", help="Enabling PAGING when viewing result"
-    )
 
     query = history_subparser.add_parser(
         "query", help="Query information for a particular build"
@@ -439,7 +464,11 @@ def history_menu(subparsers):
 
 
 def build_menu(subparsers):
-    """This method implements command line menu for ``buildtest build`` command."""
+    """This method implements command line menu for ``buildtest build`` command.
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object to add subparser
+    """
 
     parser_build = subparsers.add_parser(
         "build", aliases=["bd"], help="Build and Run test"
@@ -530,16 +559,6 @@ def build_menu(subparsers):
         type=str,
         help="Specify project account used to charge batch jobs (applicable for batch jobs only)",
     )
-    extra_group.add_argument(
-        "--disable-executor-check",
-        action="store_false",
-        help="Disable executor check during configuration check. By default these checks are enforced for Local, Slurm, PBS, LSF, and Cobalt Executor.",
-    )
-    extra_group.add_argument(
-        "--remove-stagedir",
-        action="store_true",
-        help="Remove stage directory after job completion.",
-    )
     batch_group.add_argument(
         "--maxpendtime",
         type=positive_number,
@@ -562,6 +581,21 @@ def build_menu(subparsers):
         nargs="+",
         type=positive_number,
     )
+    extra_group.add_argument(
+        "--disable-executor-check",
+        action="store_false",
+        help="Disable executor check during configuration check. By default these checks are enforced for Local, Slurm, PBS, LSF, and Cobalt Executor.",
+    )
+    extra_group.add_argument(
+        "--limit",
+        type=positive_number,
+        help="Limit number of tests that can be run.",
+    )
+    extra_group.add_argument(
+        "--remove-stagedir",
+        action="store_true",
+        help="Remove stage directory after job completion.",
+    )
     extra_group.add_argument(
         "--rebuild",
         type=positive_number,
@@ -590,8 +624,13 @@ def build_menu(subparsers):
     )
 
 
-def buildspec_menu(subparsers):
-    """This method implements ``buildtest buildspec`` command"""
+def buildspec_menu(subparsers, parent_parser):
+    """This method implements ``buildtest buildspec`` command
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object
+        parent_parser (argparse.ArgumentParser): Parent parser object
+    """
 
     parser_buildspec = subparsers.add_parser(
         "buildspec", aliases=["bc"], help="Buildspec Interface"
@@ -626,7 +665,10 @@ def buildspec_menu(subparsers):
     # buildtest buildspec find
 
     buildspec_find = subparsers_buildspec.add_parser(
-        "find", aliases=["f"], help="Query information from buildspecs cache"
+        "find",
+        aliases=["f"],
+        help="Query information from buildspecs cache",
+        parents=[parent_parser],
     )
 
     # buildtest buildspec maintainers
@@ -759,7 +801,9 @@ def buildspec_menu(subparsers):
         help="Limit number of entries queried in output",
     )
     buildspec_find.add_argument(
-        "--pager", action="store_true", help="Enable PAGING when viewing result"
+        "--row-count",
+        action="store_true",
+        help="Print total count of records from the table.",
     )
     buildspec_find.add_argument(
         "-r",
@@ -816,12 +860,11 @@ def buildspec_menu(subparsers):
     )
 
     # buildtest buildspec summary
-    buildspec_summary = subparsers_buildspec.add_parser(
-        "summary", aliases=["sm"], help="Print summary of buildspec cache"
-    )
-    # buildtest buildspec summary options
-    buildspec_summary.add_argument(
-        "-p", "--pager", action="store_true", help="Enable PAGING when viewing result"
+    subparsers_buildspec.add_parser(
+        "summary",
+        aliases=["sm"],
+        help="Print summary of buildspec cache",
+        parents=[parent_parser],
     )
     # buildtest buildspec validate
     buildspec_validate = subparsers_buildspec.add_parser(
@@ -860,11 +903,18 @@ def buildspec_menu(subparsers):
     )
 
 
-def config_menu(subparsers):
-    """This method adds argparse argument for ``buildtest config``"""
+def config_menu(subparsers, parent_parser):
+    """This method adds argparse argument for ``buildtest config``
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object
+        parent_parser (argparse.ArgumentParser): Parent parser object
+    """
 
     parser_config = subparsers.add_parser(
-        "config", aliases=["cg"], help="Query buildtest configuration"
+        "config",
+        aliases=["cg"],
+        help="Query buildtest configuration",
     )
 
     subparsers_config = parser_config.add_subparsers(
@@ -895,7 +945,7 @@ def config_menu(subparsers):
         "validate", help="Validate buildtest settings file with schema."
     )
     view_parser = subparsers_config.add_parser(
-        "view", aliases=["v"], help="View configuration file"
+        "view", aliases=["v"], help="View configuration file", parents=[parent_parser]
     )
     view_parser.add_argument(
         "-t",
@@ -904,9 +954,6 @@ def config_menu(subparsers):
         help="Specify a color theme, Pygments style to use when displaying output. See https://pygments.org/docs/styles/#getting-a-list-of-available-styles for available themes",
         choices=list(STYLE_MAP.keys()),
     )
-    view_parser.add_argument(
-        "-p", "--pager", action="store_true", help="Enable PAGING when viewing result"
-    )
     executor_group = executors.add_mutually_exclusive_group()
 
     # buildtest config executors
@@ -976,11 +1023,16 @@ def config_menu(subparsers):
     )
 
 
-def report_menu(subparsers):
-    """This method implements the ``buildtest report`` command options"""
+def report_menu(subparsers, parent_parser):
+    """This method implements the ``buildtest report`` command options
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object
+        parent_parser (argparse.ArgumentParser): Parent parser object
+    """
 
     parser_report = subparsers.add_parser(
-        "report", aliases=["rt"], help="Query test report"
+        "report", aliases=["rt"], help="Query test report", parents=[parent_parser]
     )
     subparsers = parser_report.add_subparsers(
         description="Fetch test results from report file and print them in table format",
@@ -993,7 +1045,7 @@ def report_menu(subparsers):
         "path", aliases=["p"], help="Print full path to the report file being used"
     )
     parser_report_summary = subparsers.add_parser(
-        "summary", aliases=["sm"], help="Summarize test report"
+        "summary", aliases=["sm"], help="Summarize test report", parents=[parent_parser]
     )
 
     # buildtest report
@@ -1086,16 +1138,18 @@ def report_menu(subparsers):
         action="store_true",
         help="Print total count of records from the table.",
     )
-    parser_report.add_argument(
-        "--pager", action="store_true", help="Enable PAGING when viewing result"
-    )
     parser_report_summary.add_argument(
         "--detailed", "-d", action="store_true", help="Enable a more detailed report"
     )
 
 
-def inspect_menu(subparsers):
-    """This method builds argument for ``buildtest inspect`` command"""
+def inspect_menu(subparsers, parent_parser):
+    """This method builds argument for ``buildtest inspect`` command
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object
+        parent_parser (argparse.ArgumentParser): Parent parser object
+    """
 
     parser_inspect = subparsers.add_parser(
         "inspect", aliases=["it"], help="Inspect a test based on NAME or ID "
@@ -1107,9 +1161,14 @@ def inspect_menu(subparsers):
         metavar="",
     )
     inspect_buildspec = subparser.add_parser(
-        "buildspec", aliases=["b"], help="Inspect a test based on buildspec"
+        "buildspec",
+        aliases=["b"],
+        help="Inspect a test based on buildspec",
+        parents=[parent_parser],
+    )
+    name = subparser.add_parser(
+        "name", aliases=["n"], help="Specify name of test", parents=[parent_parser]
     )
-    name = subparser.add_parser("name", aliases=["n"], help="Specify name of test")
     query_list = subparser.add_parser(
         "query", aliases=["q"], help="Query fields from record"
     )
@@ -1129,6 +1188,7 @@ def inspect_menu(subparsers):
         "list",
         aliases=["l"],
         help="List all test names, ids, and corresponding buildspecs",
+        parents=[parent_parser],
     )
     inspect_list.add_argument(
         "-n",
@@ -1160,13 +1220,23 @@ def inspect_menu(subparsers):
     query_list.add_argument(
         "-t", "--testpath", action="store_true", help="Print content of testpath"
     )
+    query_list.add_argument(
+        "--theme",
+        metavar="Color Themes",
+        help="Specify a color theme, Pygments style to use when displaying output. See https://pygments.org/docs/styles/#getting-a-list-of-available-styles for available themese",
+        choices=list(STYLE_MAP.keys()),
+    )
     query_list.add_argument(
         "name", nargs="*", help="Name of builder to query in report file"
     )
 
 
 def schema_menu(subparsers):
-    """This method builds menu for ``buildtest schema``"""
+    """This method builds menu for ``buildtest schema``
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object
+    """
 
     parser_schema = subparsers.add_parser(
         "schema", help="List schema contents and examples"
@@ -1190,7 +1260,11 @@ def schema_menu(subparsers):
 
 
 def cdash_menu(subparsers):
-    """This method builds arguments for ``buildtest cdash`` command."""
+    """This method builds arguments for ``buildtest cdash`` command.
+
+    Args:
+        subparsers (argparse._SubParsersAction): Subparser object
+    """
 
     parser_cdash = subparsers.add_parser("cdash", help="Upload test to CDASH server")
 
diff --git a/buildtest/cli/build.py b/buildtest/cli/build.py
index e316f1abd..aa4f4a931 100644
--- a/buildtest/cli/build.py
+++ b/buildtest/cli/build.py
@@ -55,6 +55,7 @@
 
 logger = logging.getLogger(__name__)
 
+
 # Context manager that copies stdout and any exceptions to a log file
 class Tee(object):
     def __init__(self, filename):
@@ -188,7 +189,6 @@ def discover_buildspecs(
     # if user pass buildspecs to be excluded (buildtest build -x <buildspec>) then
     # discover all excluded buildspecs and remove from discovered list
     if exclude_buildspecs:
-
         # discover all excluded buildspecs, if its file add to list,
         # if its directory traverse all .yml files
         for name in exclude_buildspecs:
@@ -242,7 +242,6 @@ def print_discovered_buildspecs(buildspec_dict):
 
     # if any buildspecs removed due to -x option we print them to screen
     if buildspec_dict["excluded"]:
-
         table = Table(
             title="Excluded buildspecs", box=box.DOUBLE_EDGE, header_style="blue"
         )
@@ -254,7 +253,6 @@ def print_discovered_buildspecs(buildspec_dict):
 
     # print breakdown of buildspecs by tags
     if buildspec_dict.get("tags"):
-
         for tagname in buildspec_dict["tags"].keys():
             table = Table(
                 title=f"Buildspecs By Tag={tagname}",
@@ -268,7 +266,6 @@ def print_discovered_buildspecs(buildspec_dict):
 
     # print breakdown of buildspecs by executors
     if buildspec_dict.get("executors"):
-
         for executorname in buildspec_dict["executors"].keys():
             table = Table(
                 title=f"Buildspecs by Executor={executorname}",
@@ -319,7 +316,6 @@ def discover_buildspecs_by_tags(buildspec_cache, tagnames):
 
         for buildspecfile in buildspec_cache["buildspecs"].keys():
             for test in buildspec_cache["buildspecs"][buildspecfile].keys():
-
                 # if input tag is not of type str we skip the tag name since it is not valid
                 if not isinstance(name, str):
                     logger.warning(f"Tag: {name} is not of type 'str'")
@@ -366,7 +362,6 @@ def discover_buildspecs_by_executor(buildspec_cache, executors):
 
         for buildspecfile in buildspec_cache["buildspecs"].keys():
             for test in buildspec_cache["buildspecs"][buildspecfile].keys():
-
                 # check if executor in buildspec matches one in argument (buildtest build --executor <EXECUTOR>)
                 if name == buildspec_cache["buildspecs"][buildspecfile][test].get(
                     "executor"
@@ -500,6 +495,7 @@ def __init__(
         rerun=None,
         executor_type=None,
         timeout=None,
+        limit=None,
     ):
         """The initializer method is responsible for checking input arguments for type
         check, if any argument fails type check we raise an error. If all arguments pass
@@ -531,6 +527,7 @@ def __init__(
             rerun (bool, optional): Rerun last successful **buildtest build** command. This is specified via ``buildtest build --rerun``. All other options will be ignored and buildtest will read buildtest options from file **BUILDTEST_RERUN_FILE**.
             executor_type (bool, optional): Filter test by executor type. This option will filter test after discovery by local or batch executors. This can be specified via ``buildtest build --exec-type``
             timeout (int, optional): Test timeout in seconds specified by ``buildtest build --timeout``
+            limit (int, optional): Limit number of tests that can be run. This option is specified by ``buildtest build --limit``
         """
 
         if buildspecs and not isinstance(buildspecs, list):
@@ -568,6 +565,12 @@ def __init__(
             if timeout <= 0:
                 raise BuildTestError("Timeout must be greater than 0")
 
+        if limit is not None:
+            if not isinstance(limit, int):
+                raise BuildTestError(f"{timeout} is not of type int")
+            if limit <= 0:
+                raise BuildTestError("Limit must be greater than 0")
+
         self.remove_stagedir = remove_stagedir
         self.configuration = configuration
         self.buildspecs = buildspecs
@@ -590,6 +593,7 @@ def __init__(
         self.numnodes = numnodes
         self.executor_type = executor_type
         self.timeout = timeout
+        self.limit = limit
 
         # this variable contains the detected buildspecs that will be processed by buildtest.
         self.detected_buildspecs = None
@@ -614,7 +618,6 @@ def __init__(
         )
 
         if self.logdir:
-
             create_dir(self.logdir)
             self.logfile.name = os.path.join(
                 self.logdir, os.path.basename(self.logfile.name)
@@ -677,7 +680,8 @@ def __init__(
     def load_rerun_file(self):
         """This will load content of file BUILDTEST_RERUN_FILE that contains a dictionary of key/value pair
         that keeps track of last ``buildtest build`` command. This is used with ``buildtest build --rerun``. Upon loading
-        file we reinitalize all class variables that store argument for ``buildtest build`` options"""
+        file we reinitalize all class variables that store argument for ``buildtest build`` options
+        """
 
         if not is_file(BUILDTEST_RERUN_FILE):
             raise BuildTestError(
@@ -715,6 +719,7 @@ def load_rerun_file(self):
         self.numprocs = content["numprocs"]
         self.executor_type = content["executor_type"]
         self.timeout = content["timeout"]
+        self.limit = content["limit"]
 
     def save_rerun_file(self):
         """Record buildtest command options and save them into rerun file which is read when invoking ``buildtest build --rerun``."""
@@ -741,6 +746,7 @@ def save_rerun_file(self):
             "numnodes": self.numnodes,
             "executor_type": self.executor_type,
             "timeout": self.timeout,
+            "limit": self.limit,
         }
 
         with open(BUILDTEST_RERUN_FILE, "w") as fd:
@@ -806,6 +812,12 @@ def build(self):
         if not self.builders or self.stage == "parse":
             return
 
+        if self.limit:
+            self.builders = self.builders[: self.limit]
+            console.print(
+                f"[red]Limit number of tests to {self.limit} for Building and Running. "
+            )
+
         self.build_phase()
 
         # if --stage=build is set  we return from method
@@ -894,14 +906,12 @@ def parse_buildspecs(self):
         console.print(f"[red]Invalid Buildspecs: {len(self.invalid_buildspecs)}")
 
         for buildspec in valid_buildspecs:
-
             msg = f"[green]{buildspec}: VALID"
             console.print(msg)
 
         # print any skipped buildspecs if they failed to validate during build stage
         if self.invalid_buildspecs:
             for buildspec in self.invalid_buildspecs:
-
                 msg = f"[red]{buildspec}: INVALID"
                 console.print(msg)
 
@@ -1322,7 +1332,6 @@ def print_batch_builders(self, builders):
     def print_builders(
         self, compiler_builder, spack_builder, script_builder, batch_builder
     ):
-
         """Print detected builders during build phase"""
 
         self.print_builders_by_type(script_builder, builder_type="script")
diff --git a/buildtest/cli/buildspec.py b/buildtest/cli/buildspec.py
index 005dd09c1..02304a12c 100644
--- a/buildtest/cli/buildspec.py
+++ b/buildtest/cli/buildspec.py
@@ -37,7 +37,6 @@
 
 
 class BuildspecCache:
-
     table = {}
     filter_fields = ["type", "executor", "tags", "buildspec"]
     default_format_fields = ["name", "type", "executor", "tags", "description"]
@@ -134,7 +133,6 @@ def load_paths(self):
         # we resolve path and if path exist add to self.paths. The path must be a
         # directory. If its file, we ignore it
         if self.roots:
-
             for root in self.roots:
                 path = resolve_path(root, exist=False)
                 if not os.path.exists(path):
@@ -231,7 +229,6 @@ class and return a list of valid buildspecs. Any invalid buildspecs are added to
                     ExecutorError,
                     ValidationError,
                 ) as err:
-
                     if isinstance(err, BuildspecError):
                         self.invalid_buildspecs[buildspec] = {
                             "msg": err.get_exception()
@@ -319,12 +316,10 @@ def build_cache(self):
 
         # for every parsers (valid buildspecs) we update cache to build an index
         for parser in parsers:
-
             recipe = parser.recipe["buildspecs"]
 
             # if maintainer field specified add all maintainers from buildspec to list
             if parser.recipe.get("maintainers"):
-
                 for author in parser.recipe["maintainers"]:
                     if not self.update_cache["maintainers"].get(author):
                         self.update_cache["maintainers"][author] = []
@@ -335,7 +330,6 @@ def build_cache(self):
                 self.update_cache["buildspecs"][parser.buildspec] = {}
 
             for name in recipe.keys():
-
                 self.update_cache["buildspecs"][parser.buildspec][name] = recipe[name]
                 tags = recipe[name].get("tags")
                 executor = recipe[name].get("executor")
@@ -389,7 +383,6 @@ def _check_filter_fields(self):
         self.type_filter = None
 
         if self.filter:
-
             filter_error = False
             # check if filter keys are accepted filter fields, if not we raise error
             for key in self.filter.keys():
@@ -417,7 +410,6 @@ def _check_format_fields(self):
             self.table[field] = []
 
         if self.format:
-
             format_error = False
             for key in self.format.split(","):
                 if key not in self.format_fields:
@@ -501,9 +493,7 @@ def find_buildspecs(self):
                 filtered_buildspecs = [buildspec]
 
         for buildspecfile in filtered_buildspecs:
-
             for test in self.cache["buildspecs"][buildspecfile].keys():
-
                 test_recipe = self.cache["buildspecs"][buildspecfile][test]
                 schema_type = test_recipe.get("type")
                 executor = test_recipe.get("executor")
@@ -537,7 +527,6 @@ def find_buildspecs(self):
                             self.table[field].append(test_recipe.get(field))
 
                 else:
-
                     self.table["name"].append(test)
                     self.table["type"].append(schema_type)
                     self.table["executor"].append(executor)
@@ -593,18 +582,18 @@ def test_breakdown_by_buildspec(self):
 
         return buildspec_summary
 
-    def print_buildspecfiles(self, terse=None, header=None):
+    def print_buildspecfiles(self, terse=None, header=None, row_count=None):
         """This method implements ``buildtest buildspec find --buildspec`` which reports all buildspec files in cache.
 
         Args:
             terse (bool, optional): This argument will print output in terse format if ``--terse`` option is specified otherwise will print output in table format
             header (bool, optional): This argument controls whether header will be printed in terse format. If ``--terse`` option is not specified this argument has no effect. This argument holds the value of ``--no-header`` option
+            row_count (bool, optional): Print total number of records from the table
         """
 
         self.terse = terse or self.terse
         self.header = header or self.header
         if self.terse:
-
             if not self.header:
                 console.print("buildspec", style=self.color)
 
@@ -627,11 +616,18 @@ def print_buildspecfiles(self, terse=None, header=None):
                 console.print(table)
             return
 
+        if row_count:
+            print(table.row_count)
+            return
+
         console.print(table)
 
-    def print_tags(self):
+    def print_tags(self, row_count=None):
         """This method implements ``buildtest buildspec find --tags`` which
         reports a list of unique tags from all buildspecs in cache file.
+
+        Args:
+            row_count (bool, optional): Print total number of records from the table
         """
 
         # if --terse option specified print list of all tags in machine readable format
@@ -658,13 +654,20 @@ def print_tags(self):
                 console.print(table)
             return
 
+        if row_count:
+            print(table.row_count)
+            return
+
         console.print(table)
 
-    def print_executors(self):
-        """This method implements ``buildtest buildspec find --executors`` which reports all executors from cache."""
+    def print_executors(self, row_count=None):
+        """This method implements ``buildtest buildspec find --executors`` which reports all executors from cache.
 
-        if self.terse:
+        Args:
+            row_count (bool, optional): Print total number of records from the table
+        """
 
+        if self.terse:
             if not self.header:
                 console.print("executor", style=self.color)
 
@@ -687,13 +690,16 @@ def print_executors(self):
                 console.print(table)
             return
 
+        if row_count:
+            print(table.row_count)
+            return
+
         console.print(table)
 
     def print_by_executors(self):
         """This method prints executors by tests and implements ``buildtest buildspec find --group-by-executor`` command"""
 
         if self.terse:
-
             if not self.header:
                 console.print("executor|name|description", style=self.color)
 
@@ -726,7 +732,6 @@ def print_by_tags(self):
         """This method prints tags by tests and implements ``buildtest buildspec find --group-by-tags`` command"""
 
         if self.terse:
-
             if not self.header:
                 console.print("tags|name|description", style=self.color)
 
@@ -751,7 +756,7 @@ def print_by_tags(self):
 
         console.print(table)
 
-    def print_buildspecs(self, terse=None, header=None, quiet=None):
+    def print_buildspecs(self, terse=None, header=None, quiet=None, row_count=None):
         """Print buildspec table. This method is typically called when running ``buildtest buildspec find`` or options
         with ``--filter`` and ``--format``.
 
@@ -759,6 +764,7 @@ def print_buildspecs(self, terse=None, header=None, quiet=None):
             terse (bool, optional): This argument will print output in terse format if ``--terse`` option is specified otherwise will print output in table format
             header (bool, optional): This argument controls whether header will be printed in terse format. If ``--terse`` option is not specified this argument has no effect. This argument holds the value of ``--no-header`` option
             quiet (bool, optional): If this option is set we return immediately and don't anything. This is specified via ``buildtest buildspec find --quiet`` which can be useful when rebuilding cache without displaying output
+            row_count (bool, optional): Print total number of records from the table
         """
 
         # Don't print anything if --quiet is set
@@ -799,7 +805,6 @@ def print_buildspecs(self, terse=None, header=None, quiet=None):
                 console.print("|".join(self.table.keys()), style=self.color)
 
             for row in t:
-
                 if not isinstance(row, list):
                     continue
 
@@ -815,6 +820,10 @@ def print_buildspecs(self, terse=None, header=None, quiet=None):
                 console.print(table)
             return
 
+        if row_count:
+            console.print(table.row_count)
+            return
+
         console.print(table)
 
     def list_maintainers(self):
@@ -1046,7 +1055,6 @@ def edit_buildspec_file(buildspecs, configuration, editor):
         editor (str): Path to editor to use when opening file
     """
     for file in buildspecs:
-
         buildspec = resolve_path(file, exist=False)
         if is_dir(buildspec):
             console.print(
@@ -1355,12 +1363,12 @@ def buildspec_find(args, configuration):
 
     # buildtest buildspec find --tags
     if args.tags:
-        cache.print_tags()
+        cache.print_tags(row_count=args.row_count)
         return
 
     # buildtest buildspec find --buildspec
     if args.buildspec:
-        cache.print_buildspecfiles()
+        cache.print_buildspecfiles(row_count=args.row_count)
         return
 
     # buildtest buildspec find --paths
@@ -1370,7 +1378,7 @@ def buildspec_find(args, configuration):
 
     # buildtest buildspec find --executors
     if args.executors:
-        cache.print_executors()
+        cache.print_executors(row_count=args.row_count)
         return
 
     # buildtest buildspec find --group-by-executors
@@ -1403,4 +1411,4 @@ def buildspec_find(args, configuration):
         cache.print_raw_format_fields()
         return
 
-    cache.print_buildspecs(quiet=args.quiet)
+    cache.print_buildspecs(quiet=args.quiet, row_count=args.row_count)
diff --git a/buildtest/cli/cdash.py b/buildtest/cli/cdash.py
index 17c58d28d..aa7e45692 100644
--- a/buildtest/cli/cdash.py
+++ b/buildtest/cli/cdash.py
@@ -53,7 +53,6 @@ def cdash_cmd(args, default_configuration=None, open_browser=True, report_file=N
         )
 
     if args.cdash == "upload":
-
         upload_test_cdash(
             build_name=args.buildname,
             configuration=configuration,
diff --git a/buildtest/cli/clean.py b/buildtest/cli/clean.py
index cc8774b28..206a03e4f 100644
--- a/buildtest/cli/clean.py
+++ b/buildtest/cli/clean.py
@@ -29,7 +29,6 @@ def clean(configuration, yes):
 
     # request user prompt when 'buildtest clean' is specified without '-y' option. Default selection is 'y' for confirmation
     if not yes:
-
         remove_testdir = Prompt.ask(
             f"Remove Test Directory {resolved_testdir}",
             default="y",
diff --git a/buildtest/cli/compilers.py b/buildtest/cli/compilers.py
index f46ab075c..27275c296 100644
--- a/buildtest/cli/compilers.py
+++ b/buildtest/cli/compilers.py
@@ -17,7 +17,6 @@
 
 
 def compiler_cmd(args, configuration):
-
     if args.compilers == "find":
         compiler_find(
             configuration,
@@ -337,7 +336,6 @@ def _validate_modules(self, module_dict):
         """
 
         if self.detailed:
-
             table = Table(
                 title="Discovered Modules", show_lines=True, header_style="blue"
             )
diff --git a/buildtest/cli/config.py b/buildtest/cli/config.py
index 54f45cd15..def8ed05c 100644
--- a/buildtest/cli/config.py
+++ b/buildtest/cli/config.py
@@ -185,7 +185,6 @@ def view_executors(
         return
 
     if disabled:
-
         if not configuration.disabled_executors:
             console.print("There are no disabled executors")
             return
@@ -195,7 +194,6 @@ def view_executors(
         return
 
     if invalid:
-
         if not configuration.invalid_executors:
             console.print("There are no invalid executors")
             return
diff --git a/buildtest/cli/help.py b/buildtest/cli/help.py
index 2cf7082c3..65827120a 100644
--- a/buildtest/cli/help.py
+++ b/buildtest/cli/help.py
@@ -83,6 +83,9 @@ def print_build_help():
         "buildtest build -b <file> --timeout 60",
         "Test will run till it reaches timeout of 60sec and then it will be cancelled if it exceeds the limit.",
     )
+    table.add_row(
+        "buildtest build -t python --limit=5", "Limit number of test runs to 5"
+    )
 
     console.print(table)
 
@@ -99,6 +102,9 @@ def print_buildspec_help():
         "Discover and validate all buildspecs and load all validated buildspecs in cache",
     )
     table.add_row("buildtest buildspec find --rebuild", "Rebuild cache file")
+    table.add_row(
+        "buildtest buildspec find --pager", "Paginate output of buildspec cache"
+    )
     table.add_row(
         "buildtest buildspec find --root /tmp --rebuild",
         "Discover buildspecs in /tmp and rebuild buildspec cache",
@@ -136,6 +142,10 @@ def print_buildspec_help():
     table.add_row("buildtest buildspec find --helpfilter", "Show all filter fields")
     table.add_row("buildtest buildspec find --helpformat", "Show all format fields")
     table.add_row("buildtest buildspec find --terse", "Display output in terse format")
+    table.add_row(
+        "buildtest buildspec find --row-count",
+        "Print total count of records from the table",
+    )
     table.add_row(
         "buildtest buildspec find --count=5",
         "Limit output of buildspec cache to 5 elements",
@@ -176,7 +186,8 @@ def print_buildspec_help():
 
     table.add_row("buildtest buildspec summary", "Show summary of buildspec cache file")
     table.add_row(
-        "buildtest buildspec summary --pager", "Pageants the output of summary"
+        "buildtest buildspec summary --pager",
+        "Paginate the output of summary for buildspec cache",
     )
     table.add_row(
         "buildtest buildspec show python_hello",
@@ -230,6 +241,9 @@ def print_config_help():
     table.add_column("Description", justify="left", style="magenta")
 
     table.add_row("buildtest config view", "View content of configuration file")
+    table.add_row(
+        "buildtest config view --pager", "Paginate output of configuration file"
+    )
     table.add_row(
         "buildtest config validate", "Validate configuration file with JSON schema"
     )
@@ -329,6 +343,7 @@ def print_report_help():
     table.add_column("Description", justify="left", style="magenta")
 
     table.add_row("buildtest report", "Display all test results")
+    table.add_row("buildtest report --pager", "Paginate output of test results")
     table.add_row(
         "buildtest report --filter returncode=0", "Filter test results by returncode=0"
     )
@@ -371,6 +386,9 @@ def print_report_help():
     table.add_row(
         "buildtest report summary --detailed", "Show detailed summary of test report"
     )
+    table.add_row(
+        "buildtest report summary --pager", "Paginate output of report summary"
+    )
     console.print(table)
 
 
diff --git a/buildtest/cli/helpcolor.py b/buildtest/cli/helpcolor.py
index 35cb802f0..214bb5c35 100644
--- a/buildtest/cli/helpcolor.py
+++ b/buildtest/cli/helpcolor.py
@@ -3,14 +3,14 @@
 from rich.table import Table
 
 
-def print_available_colors():
+def print_available_colors(color_names=ANSI_COLOR_NAMES):
     """Print the available color options in a table format on background of the color option."""
     table = Table(
         "Number",
         "Color",
-        header_style="blue",
+        header_style="bold",
         title="Available Colors",
     )
-    for color, number in ANSI_COLOR_NAMES.items():
+    for color, number in color_names.items():
         table.add_row(f"[black]{number}", f"[black]{color}", style="on " + color)
     console.print(table)
diff --git a/buildtest/cli/history.py b/buildtest/cli/history.py
index 0fa2ff166..b8379ef28 100644
--- a/buildtest/cli/history.py
+++ b/buildtest/cli/history.py
@@ -101,7 +101,6 @@ def list_build_history(no_header=None, terse=None, pager=None, color=None):
         table["fail_rate"].append(content["test_summary"]["fail_rate"])
 
     if terse:
-
         row_entry = []
 
         for key in table.keys():
diff --git a/buildtest/cli/info.py b/buildtest/cli/info.py
index 54bf51d24..9a5a5c6de 100644
--- a/buildtest/cli/info.py
+++ b/buildtest/cli/info.py
@@ -25,61 +25,66 @@ def buildtest_info(configuration, buildtest_system):
 
     be = BuildExecutor(configuration)
 
-    buildtest_details = f"""
-[red]Buildtest Version:[/red]        [green]{BUILDTEST_VERSION}[/green]
-[red]Buildtest Path:[/red]           [green]{shutil.which('buildtest')}[/green]
-[red]Configuration File:[/red]       [green]{configuration.file}[/green]
-[red]Available Systems:[/red]        [green]{configuration.systems}[/green]
-[red]Active System:[/red]            [green]{configuration.name()}[/green]
-[red]Available Executors:[/red]      [green]{be.names()}[/green]
-"""
-
-    system_details = f"""
-[red]Python Path:[/red]         [green]{buildtest_system.system['python']}[/green]
-[red]Python Version:[/red]      [green]{buildtest_system.system['pyver']}[/green]
-[red]Processor:[/red]           [green]{buildtest_system.system['processor']}[/green]
-[red]Host:[/red]                [green]{buildtest_system.system['host']}[/green]
-[red]Machine:[/red]             [green]{buildtest_system.system['machine']}[/green]
-[red]Operating System:[/red]    [green]{buildtest_system.system['os']}[/green]
-[red]Module System:[/red]       [green]{buildtest_system.system['moduletool']}[/green]
-"""
+    buildtest_details = [
+        f"[red]Buildtest Version:[/red]        [green]{BUILDTEST_VERSION}[/green]",
+        f"[red]Buildtest Path:[/red]           [green]{shutil.which('buildtest')}[/green]",
+        f"[red]Configuration File:[/red]       [green]{configuration.file}[/green]",
+        f"[red]Available Systems:[/red]        [green]{configuration.systems}[/green]",
+        f"[red]Active System:[/red]            [green]{configuration.name()}[/green]",
+        f"[red]Available Executors:[/red]      [green]{be.names()}[/green]",
+    ]
+    system_details = [
+        f"[red]Python Path:[/red]         [green]{buildtest_system.system['python']}[/green]",
+        f"[red]Python Version:[/red]      [green]{buildtest_system.system['pyver']}[/green]",
+        f"[red]Processor:[/red]           [green]{buildtest_system.system['processor']}[/green]",
+        f"[red]Host:[/red]                [green]{buildtest_system.system['host']}[/green]",
+        f"[red]Machine:[/red]             [green]{buildtest_system.system['machine']}[/green]",
+        f"[red]Operating System:[/red]    [green]{buildtest_system.system['os']}[/green]",
+        f"[red]Module System:[/red]       [green]{buildtest_system.system['moduletool']}[/green]",
+    ]
 
     if is_dir(BUILD_HISTORY_DIR):
-        buildtest_details += f"[red]Build History Directory:[/red]  [green]{BUILD_HISTORY_DIR}[/green] \n"
-        buildtest_details += f"[red]Number of builds:[/red]         [green]{len(os.listdir(BUILD_HISTORY_DIR))}[/green] \n"
+        buildtest_details.extend(
+            [
+                f"[red]Build History Directory:[/red]  [green]{BUILD_HISTORY_DIR}[/green]",
+                f"[red]Number of builds:[/red]         [green]{len(os.listdir(BUILD_HISTORY_DIR))}[/green]",
+            ]
+        )
 
     if is_file(BUILDSPEC_CACHE_FILE):
-        buildtest_details += f"[red]Buildspec Cache File:[/red]     [green]{BUILDSPEC_CACHE_FILE}[/green] \n"
+        buildtest_details.append(
+            f"[red]Buildspec Cache File:[/red]     [green]{BUILDSPEC_CACHE_FILE}[/green]"
+        )
     else:
-        buildtest_details += "[red]Buildspec Cache File does not exist"
+        buildtest_details.append("[red]Buildspec Cache File does not exist")
 
     if is_file(BUILD_REPORT):
-        buildtest_details += (
+        buildtest_details.append(
             f"[red]Default Report File:[/red]      [green]{BUILD_REPORT}[/green]"
         )
-
     else:
-        buildtest_details += "[red]Default report file does not exist"
+        buildtest_details.append("[red]Default report file does not exist")
 
     console.print(
-        Panel.fit(buildtest_details, title="buildtest details"), justify="left"
+        Panel.fit("\n".join(buildtest_details), title="buildtest details"),
+        justify="left",
+    )
+    console.print(
+        Panel.fit("\n".join(system_details), title="system details"), justify="left"
     )
-    console.print(Panel.fit(system_details, title="system details"), justify="left")
-
-    cmd = BuildTestCommand("black --version")
-    cmd.execute()
 
-    console.print(f"black: {shutil.which('black')}")
-    console.print(f"black version: {''.join(cmd.get_output())}")
+    for package in ["black", "pyflakes", "isort"]:
+        print_version_info(package)
 
-    cmd = BuildTestCommand("pyflakes --version")
-    cmd.execute()
 
-    console.print(f"pyflakes: {shutil.which('pyflakes')}")
-    console.print(f"pyflakes version: {''.join(cmd.get_output())}")
+def print_version_info(command_name):
+    """Print version information for any command by running --version
 
-    cmd = BuildTestCommand("isort --version")
+    Args:
+        command_name (str): Name of command to run with --version
+    """
+    cmd = BuildTestCommand(f"{command_name} --version")
     cmd.execute()
 
-    console.print(f"isort: {shutil.which('isort')}")
-    console.print(f"{' '.join(cmd.get_output())}")
+    console.print(f"{command_name}: {shutil.which(command_name)}")
+    console.print(f"{command_name} version: {''.join(cmd.get_output())}")
diff --git a/buildtest/cli/inspect.py b/buildtest/cli/inspect.py
index 03adbaf65..ad0412272 100644
--- a/buildtest/cli/inspect.py
+++ b/buildtest/cli/inspect.py
@@ -48,6 +48,10 @@ def inspect_cmd(args, report_file=None):
 def fetch_test_names(report, names):
     """Return a list of builders given input test names by search the report file for valid records. If test is found it will be returned as a builder name. If names
     are specified without test ID then we retrieve latest record for test name. If names are specified with ID we find the first matching test record.
+
+    Args:
+        report (buildtest.cli.report.Report): An instance of Report class
+        names (list): A list of test names
     """
     query_builders = []
     name_lookup = report.lookup()
@@ -159,6 +163,8 @@ def inspect_query(report, args):
     for name, test_record in records.items():
         for tests in test_record:
             for full_id, test in tests.items():
+                theme = args.theme or "monokai"
+
                 console.rule(f"[cyan]{name}/{full_id}")
 
                 console.print(f"[blue]Executor: {test['executor']}")
@@ -184,7 +190,6 @@ def inspect_query(report, args):
 
                 # print content of output file when 'buildtest inspect query --output' is set
                 if args.output:
-
                     content = read_file(test["outfile"])
                     console.rule(f"Output File: {test['outfile']}")
 
@@ -196,7 +201,7 @@ def inspect_query(report, args):
                     content = read_file(test["errfile"])
                     console.rule(f"Error File: {test['errfile']}")
 
-                    syntax = Syntax(content, "text")
+                    syntax = Syntax(content, "text", theme=theme)
                     console.print(syntax)
 
                 # print content of testpath when 'buildtest inspect query --testpath' is set
@@ -204,7 +209,7 @@ def inspect_query(report, args):
                     content = read_file(test["testpath"])
                     console.rule(f"Test File: {test['testpath']}")
 
-                    syntax = Syntax(content, "shell", theme="emacs")
+                    syntax = Syntax(content, "shell", theme=theme)
                     console.print(syntax)
 
                 # print content of build script when 'buildtest inspect query --buildscript' is set
@@ -212,14 +217,14 @@ def inspect_query(report, args):
                     content = read_file(test["build_script"])
                     console.rule(f"Test File: {test['build_script']}")
 
-                    syntax = Syntax(content, lexer="shell", theme="emacs")
+                    syntax = Syntax(content, lexer="shell", theme=theme)
                     console.print(syntax)
 
                 if args.buildenv:
                     content = read_file(test["buildenv"])
                     console.rule(f"Test File: {test['buildenv']}")
 
-                    syntax = Syntax(content, lexer="text", theme="emacs")
+                    syntax = Syntax(content, lexer="text", theme=theme)
                     console.print(syntax)
 
 
diff --git a/buildtest/cli/path.py b/buildtest/cli/path.py
index c42bbd0fc..09d64a7cf 100644
--- a/buildtest/cli/path.py
+++ b/buildtest/cli/path.py
@@ -53,7 +53,6 @@ def path_cmd(
 
     # if input name contains a '/' followed by TEST ID we will match id
     if name.find("/") != -1:
-
         for builder in builders:
             if builder.startswith(name):
                 tid = builder.split("/")[1]
diff --git a/buildtest/cli/report.py b/buildtest/cli/report.py
index 2028190e9..5bbfb6c3c 100644
--- a/buildtest/cli/report.py
+++ b/buildtest/cli/report.py
@@ -153,7 +153,6 @@ def _check_filter_fields(self):
 
         # check if filter arguments (--filter) are valid fields
         if self.filter:
-
             logger.debug(f"Checking filter fields: {self.filter}")
 
             # check if filter keys are accepted filter fields, if not we raise error
@@ -185,7 +184,6 @@ def _check_format_fields(self):
         # if buildtest report --format specified split field by "," and validate each
         # format field and reassign display_table
         if self.format:
-
             logger.debug(f"Checking format fields: {self.format}")
 
             self.display_format_fields = self.format.split(",")
@@ -210,7 +208,6 @@ def _check_start_and_end_fields(self):
         """
 
         if self.end:
-
             current_time = datetime.datetime.now()
             logger.debug(f"checking end field: {self.end}")
 
@@ -403,10 +400,8 @@ def _filter_by_returncode(self, test):
     def process_report(self):
         # process all filtered buildspecs and add rows to display_table.
         for buildspec in self.filtered_buildspecs:
-
             # process each test in buildspec file
             for name in self.report[buildspec].keys():
-
                 if self.filter:
                     if self._filter_by_names(name):
                         continue
@@ -438,7 +433,6 @@ def process_report(self):
                 # process all tests for an associated script. There can be multiple
                 # test runs for a single test depending on how many tests were run
                 for test in tests:
-
                     if self.filter:
                         # filter by tags, if filter tag not found in test tag list we skip test
                         if self._filter_by_tags(test):
@@ -502,7 +496,6 @@ def print_filter_fields(self):
         )
 
         for field, value in self.filter_field_description.items():
-
             table.add_row(
                 f"[red]{field}",
                 f"[green]{value['description']}",
diff --git a/buildtest/cli/schema.py b/buildtest/cli/schema.py
index 80fa224d5..715c610d2 100644
--- a/buildtest/cli/schema.py
+++ b/buildtest/cli/schema.py
@@ -46,7 +46,6 @@ def schema_cmd(args):
 
     schema_examples = walk_tree(examples, ".yml")
     for example in schema_examples:
-
         if args.example:
             content = read_file(example)
             console.rule(example)
diff --git a/buildtest/config.py b/buildtest/config.py
index bf8e0e134..8ca7ecba6 100644
--- a/buildtest/config.py
+++ b/buildtest/config.py
@@ -158,7 +158,6 @@ def _executor_check(self):
         self._validate_pbs_executors()
 
     def is_executor_disabled(self, executor):
-
         if executor.get("disable"):
             return True
 
@@ -217,7 +216,6 @@ def _validate_lsf_executors(self):
 
         # check all executors have defined valid queues and check queue state.
         for executor in lsf_executors:
-
             executor_name = f"{self.name()}.{executor_type}.{executor}"
             if self.is_executor_disabled(lsf_executors[executor]):
                 self.disabled_executors.append(executor_name)
@@ -235,7 +233,6 @@ def _validate_lsf_executors(self):
 
                 # check queue record for Status
                 for name in record:
-
                     # skip record until we find matching queue
                     if name["QUEUE_NAME"] != queue:
                         continue
@@ -271,7 +268,6 @@ def _validate_slurm_executors(self):
             return
 
         for executor in slurm_executor:
-
             executor_name = f"{self.name()}.{executor_type}.{executor}"
 
             if self.is_executor_disabled(slurm_executor[executor]):
@@ -280,7 +276,6 @@ def _validate_slurm_executors(self):
 
             # if 'partition' key defined check if its valid partition
             if slurm_executor[executor].get("partition"):
-
                 if slurm_executor[executor]["partition"] not in slurm.partitions:
                     self.invalid_executors(executor_name)
                     logger.error(
@@ -409,7 +404,6 @@ def _validate_pbs_executors(self):
             return
 
         for executor in pbs_executor:
-
             executor_name = f"{self.name()}.{executor_type}.{executor}"
 
             if self.is_executor_disabled(pbs_executor[executor]):
@@ -428,7 +422,6 @@ def _validate_pbs_executors(self):
                 pbs.queue_summary["Queue"][queue]["enabled"] != "True"
                 or pbs.queue_summary["Queue"][queue]["started"] != "True"
             ):
-
                 self.invalid_executors.append(executor_name)
                 logger.info("Queue configuration")
                 logger.info(json.dumps(pbs.queue_summary, indent=2))
diff --git a/buildtest/exceptions.py b/buildtest/exceptions.py
index 2a6c8bccc..0ed7c9693 100644
--- a/buildtest/exceptions.py
+++ b/buildtest/exceptions.py
@@ -27,7 +27,6 @@ class BuildspecError(Exception):
     """Exception if there is an issue with parsing a Buildspec or building test"""
 
     def __init__(self, msg, buildspec=None):
-
         self.msg = msg
         if buildspec:
             self.msg = f"[{buildspec}]: {msg}"
diff --git a/buildtest/executors/cobalt.py b/buildtest/executors/cobalt.py
index b54bebdcf..58b6c5257 100644
--- a/buildtest/executors/cobalt.py
+++ b/buildtest/executors/cobalt.py
@@ -31,14 +31,12 @@ class CobaltExecutor(BaseExecutor):
     def __init__(
         self, name, settings, site_configs, account=None, maxpendtime=None, timeout=None
     ):
-
         self.account = account
         self.maxpendtime = maxpendtime
         super().__init__(name, settings, site_configs, timeout=timeout)
         self.queue = self._settings.get("queue")
 
     def launcher_command(self, numprocs, numnodes):
-
         batch_cmd = ["qsub"]
 
         if self.queue:
diff --git a/buildtest/executors/local.py b/buildtest/executors/local.py
index aac18ae7a..953c86521 100644
--- a/buildtest/executors/local.py
+++ b/buildtest/executors/local.py
@@ -22,7 +22,6 @@ class LocalExecutor(BaseExecutor):
     type = "local"
 
     def load(self):
-
         self.shell = shlex.split(self._settings["shell"])[0]
         shell_options = shlex.split(self._settings["shell"])[1:]
 
diff --git a/buildtest/executors/lsf.py b/buildtest/executors/lsf.py
index 5cd58ecac..9409cb33c 100644
--- a/buildtest/executors/lsf.py
+++ b/buildtest/executors/lsf.py
@@ -134,7 +134,6 @@ def poll(self, builder):
 
         builder.stop()
         if builder.job.is_suspended() or builder.job.is_pending():
-
             self.logger.debug(f"Time Duration: {builder.duration}")
             self.logger.debug(f"Max Pend Time: {self.maxpendtime}")
 
diff --git a/buildtest/executors/pbs.py b/buildtest/executors/pbs.py
index 692ed295b..a46f1dd96 100644
--- a/buildtest/executors/pbs.py
+++ b/buildtest/executors/pbs.py
@@ -27,7 +27,6 @@ class PBSExecutor(BaseExecutor):
     def __init__(
         self, name, settings, site_configs, account=None, maxpendtime=None, timeout=None
     ):
-
         self.maxpendtime = maxpendtime
         self.account = account
         super().__init__(name, settings, site_configs, timeout=timeout)
diff --git a/buildtest/executors/setup.py b/buildtest/executors/setup.py
index b3ab2a237..67866af2f 100644
--- a/buildtest/executors/setup.py
+++ b/buildtest/executors/setup.py
@@ -222,7 +222,6 @@ def select_builders_to_run(self, builders):
         # any test. Test will be executed async but when checking if test is complete via
         # .is_complete() we need the builders to be processed to get updated state.
         for builder in builders:
-
             if not builder.recipe.get("needs"):
                 console.print(
                     f"[green]{builder} does not have any dependencies adding test to queue"
@@ -233,7 +232,6 @@ def select_builders_to_run(self, builders):
             builder.dependency = False
             #
             for name in builder.recipe["needs"]:
-
                 # if element in needs is a string then we check if job is complete
                 if isinstance(name, str):
                     if name not in list(testnames.keys()):
@@ -259,14 +257,12 @@ def select_builders_to_run(self, builders):
                         break
 
                     if "state" in name[testname]:
-
                         match_state = (
                             name[testname]["state"]
                             == testnames[testname].metadata["result"]["state"]
                         )
 
                         if not match_state:
-
                             if testnames[testname].is_pending():
                                 builder.dependency = True
                                 console.print(
@@ -294,7 +290,6 @@ def select_builders_to_run(self, builders):
                             not in rc
                         )
                         if no_match:
-
                             if testnames[testname].is_pending():
                                 console.print(
                                     f"[red]{builder} is cancelled because it expects one of these returncode {rc} from {testnames[testname]} but test has {testnames[testname].metadata['result']['returncode']} "
@@ -344,7 +339,6 @@ def run(self, builders):
         console.print(f"Spawning {num_workers} processes for processing builders")
         count = 0
         while True:
-
             active_builders = []
             count += 1
             console.rule(f"Iteration {count}")
@@ -417,7 +411,6 @@ def poll(self, pending_jobs):
 
             # for every pending job poll job and mark if job is finished or cancelled
             for job in jobs:
-
                 # get executor instance for corresponding builder. This would be one of the following: SlurmExecutor, PBSExecutor, LSFExecutor, CobaltExecutor
                 executor = self.get(job.executor)
                 # if builder is local executor we shouldn't be polling so we set job to
@@ -455,7 +448,6 @@ def print_job_details(self, active_jobs):
             completed_jobs_table.add_column(column)
 
         for builder in active_jobs:
-
             if builder.job.is_pending() or builder.job.is_suspended():
                 pending_jobs_table.add_row(
                     f"[blue]{str(builder)}",
diff --git a/buildtest/executors/slurm.py b/buildtest/executors/slurm.py
index 683903660..56c21fce3 100644
--- a/buildtest/executors/slurm.py
+++ b/buildtest/executors/slurm.py
@@ -30,7 +30,6 @@ class SlurmExecutor(BaseExecutor):
     def __init__(
         self, name, settings, site_configs, account=None, maxpendtime=None, timeout=None
     ):
-
         self.maxpendtime = maxpendtime
         self.account = account
         super().__init__(name, settings, site_configs, timeout=timeout)
@@ -129,7 +128,6 @@ def poll(self, builder):
 
         # if job state in PENDING check if we need to cancel job by checking internal timer
         if builder.job.is_pending() or builder.job.is_suspended():
-
             self.logger.debug(f"Time Duration: {builder.duration}")
             self.logger.debug(f"Max Pend Time: {self.maxpendtime}")
 
diff --git a/buildtest/main.py b/buildtest/main.py
index 9c6fdd058..28cfa624b 100644
--- a/buildtest/main.py
+++ b/buildtest/main.py
@@ -79,7 +79,6 @@ def main():
 
     # print content of BUILDTEST_LOGFILE if buildtest --view-log or buildtest --print-log is specified which should contain output of last log
     if args.view_log or args.print_log:
-
         # if logfile is not present we should raise exception since this file is only created upon running 'buildtest build'
         if not is_file(BUILDTEST_LOGFILE):
             raise BuildTestError(
@@ -172,6 +171,7 @@ def main():
                 rerun=args.rerun,
                 executor_type=args.executor_type,
                 timeout=args.timeout,
+                limit=args.limit,
             )
             cmd.build()
 
@@ -185,7 +185,6 @@ def main():
 
     # implementation for 'buildtest buildspec find'
     elif args.subcommands in ["buildspec", "bc"]:
-
         if args.buildspecs_subcommand in ["find", "f"]:
             buildspec_find(args=args, configuration=configuration)
         elif args.buildspecs_subcommand in ["summary", "sm"]:
@@ -272,7 +271,6 @@ def main():
 
     # running buildtest cdash
     elif args.subcommands == "cdash":
-
         cdash_config = deep_get(configuration.target_config, "cdash")
 
         if not cdash_config:
diff --git a/buildtest/schemas/definitions.schema.json b/buildtest/schemas/definitions.schema.json
index b1d329569..7935f2c10 100644
--- a/buildtest/schemas/definitions.schema.json
+++ b/buildtest/schemas/definitions.schema.json
@@ -40,6 +40,28 @@
         }
       ]
     },
+    "file_regex": {
+      "type": "array",
+      "description": "Specify a list of regular expressions to match files in the current working directory. The regular expression is matched using ``re.search`` python module. ",
+      "items": {
+        "type": "object",
+        "additionalProperties": false,
+        "required": [
+          "file",
+          "exp"
+        ],
+        "properties": {
+          "file": {
+              "type": "string",
+              "description": "Specify a file name to match with regular expression"
+          },
+          "exp": {
+              "type": "string",
+              "description": "Specify a regular expression to run on the selected file name"
+          }
+        }
+      }
+    },
     "regex": {
       "type": "object",
       "description": "Perform regular expression search using ``re.search`` python module on stdout/stderr stream for reporting if test ``PASS``. ",
@@ -200,6 +222,11 @@
           "$ref": "#/definitions/regex",
           "description": "Determine state (PASS/FAIL) of test based on regular expression on output or error stream"
         },
+        "file_regex": {
+          "$ref": "#/definitions/file_regex",
+          "description": "Perform a regular expression search on a list of arbitrary files, if expression matches for any file buildtest will report as ``PASS``. This field is useful for checking output of a test that is not captured in stdout or stderr. "
+        },
+
         "runtime": {
           "type": "object",
           "description": "The runtime section will pass test based on min and max values and compare with actual runtime. ",
@@ -282,6 +309,28 @@
             }
           }
         },
+        "assert_lt": {
+          "type": "array",
+          "description": "Perform assertion of less than (<) with reference value",
+          "items": {
+            "type": "object",
+            "additionalProperties": false,
+            "required": [
+              "name",
+              "ref"
+            ],
+            "properties": {
+              "name": {
+                "type": "string",
+                "description": "Name of metric to use for comparison"
+              },
+              "ref": {
+                "type": "number",
+                "description": "Specify reference value (int,float) for comparison"
+              }
+            }
+          }
+        },
         "assert_eq":
         {
           "description": "Perform assertion of equality (=) with reference value",
@@ -393,7 +442,7 @@
             }
           }
         },
-         "not_contains": {
+        "not_contains": {
           "description": "Check if metric value not in a list of reference values ",
           "items": {
             "type": "object",
@@ -415,6 +464,10 @@
             }
           }
         },
+        "is_symlink": {
+          "description": "Check for list of files or directory paths that are symbolic links",
+          "$ref": "#/definitions/list_of_strings"
+        },
         "exists": {
           "description": "Check for list of file or directory path for existences using os.path.exists",
           "$ref": "#/definitions/list_of_strings"
@@ -427,6 +480,58 @@
           "description": "Check for list of filepaths are files",
           "$ref": "#/definitions/list_of_strings"
         },
+        "file_count": {
+          "description": "Perform assertion check by comparing file count in a directory",
+          "type": "array",
+          "items": {
+            "type": "object",
+            "additionalProperties": false,
+            "required": [
+              "dir",
+              "count"
+            ],
+            "properties": {
+              "dir": {
+                "type": "string",
+                "description": "Directory path to check for file count"
+              },
+              "count": {
+                "type": "integer",
+                "description": "Number of files expected in directory",
+                "minimum": 0
+              },
+              "depth": {
+                "type": "integer",
+                "description": "Depth of directory to search for files",
+                "minimum": 0
+              },
+              "ext": {
+                "$ref": "#/definitions/string_or_list",
+                "description": "File extension to filter files in directory"
+              },
+              "filepattern": {
+                "type": "string",
+                "description": "Specify a regular expression when searching for files in directory"
+              },
+              "filetype": {
+                "type": "string",
+                "description": "Specify file type when searching for files in directory. It can be 'file', 'dir' or 'symlink'",
+                "enum": [
+                  "file",
+                  "dir",
+                  "symlink"
+                ]
+              },
+              "file_traverse_limit": {
+                "type": "integer",
+                "description": "Limit the number of files to traverse when searching for files in directory",
+                "minimum": 1,
+                "default": 10000,
+                "maximum": 999999
+              }
+            }
+          }
+        },
         "state": {
           "$ref": "#/definitions/state",
           "description": "explicitly mark state of test regardless of status calculation"
diff --git a/buildtest/schemas/examples/compiler.schema.json/invalid/examples.yml b/buildtest/schemas/examples/compiler.schema.json/invalid/examples.yml
index 43feabd63..37eb0ed14 100644
--- a/buildtest/schemas/examples/compiler.schema.json/invalid/examples.yml
+++ b/buildtest/schemas/examples/compiler.schema.json/invalid/examples.yml
@@ -200,4 +200,4 @@ buildspecs:
           FOO: BAR
           fflags: "-O1"
           module:
-            swap: [PrgEnv-intel, PrgEnv-cray/2.6.2]
\ No newline at end of file
+            swap: [PrgEnv-intel, PrgEnv-cray/2.6.2]
diff --git a/buildtest/schemas/examples/compiler.schema.json/valid/examples.yml b/buildtest/schemas/examples/compiler.schema.json/valid/examples.yml
index 9e227fc0b..7a1bc91ab 100644
--- a/buildtest/schemas/examples/compiler.schema.json/valid/examples.yml
+++ b/buildtest/schemas/examples/compiler.schema.json/valid/examples.yml
@@ -97,7 +97,7 @@ buildspecs:
         cray:
           fflags: "-O1"
         all:
-          sbatch: ["-t 10", "-n 2", "-C haswell" ]
+          sbatch: ["-t 10", "-n 2", "-C haswell"]
       config:
         PrgEnv-cray@2.6.2:
           module:
@@ -141,10 +141,10 @@ buildspecs:
       default:
         gcc:
           fflags: "-O1"
-          sbatch: ["-t 10", "-n 2", "-C haswell" ]
+          sbatch: ["-t 10", "-n 2", "-C haswell"]
         intel:
           fflags: "-O2"
-          sbatch: ["-t 10", "-n 2", "-C knl" ]
+          sbatch: ["-t 10", "-n 2", "-C knl"]
       config:
         gcc@8.1.0:
           sbatch: ["-t 60", "-n 2", "-C knl"]
@@ -162,15 +162,15 @@ buildspecs:
       default:
         gcc:
           fflags: "-O1"
-          bsub: ["-W 00:30", "-n 2" ]
+          bsub: ["-W 00:30", "-n 2"]
         intel:
           fflags: "-O2"
-          bsub: ["-W 00:30", "-n 4" ]
+          bsub: ["-W 00:30", "-n 4"]
       config:
         gcc@8.1.0:
-           bsub: ["-W 00:30", "-n 6" ]
-           module:
-             swap: [PrgEnv-intel, PrgEnv-gnu/6.1.0]
+          bsub: ["-W 00:30", "-n 6"]
+          module:
+            swap: [PrgEnv-intel, PrgEnv-gnu/6.1.0]
 
   env_example:
     type: compiler
diff --git a/buildtest/schemas/examples/global.schema.json/invalid/exceed_testname_length.yml b/buildtest/schemas/examples/global.schema.json/invalid/exceed_testname_length.yml
index 5c8a0c146..6f8311bd5 100644
--- a/buildtest/schemas/examples/global.schema.json/invalid/exceed_testname_length.yml
+++ b/buildtest/schemas/examples/global.schema.json/invalid/exceed_testname_length.yml
@@ -3,4 +3,4 @@ buildspecs:
   _________this_test_exceeds_character_length______________:
     type: script
     run: hostname
-    executor: generic.local.bash
\ No newline at end of file
+    executor: generic.local.bash
diff --git a/buildtest/schemas/examples/global.schema.json/invalid/invalid_pattern.yml b/buildtest/schemas/examples/global.schema.json/invalid/invalid_pattern.yml
index 385313a26..579003a2d 100644
--- a/buildtest/schemas/examples/global.schema.json/invalid/invalid_pattern.yml
+++ b/buildtest/schemas/examples/global.schema.json/invalid/invalid_pattern.yml
@@ -2,4 +2,4 @@ buildspecs:
   # invalid pattern for test. Must be matching regex "^[A-Za-z_.][A-Za-z0-9_]*$" when declaring a dict
   (badname:
     type: script
-    run: "ping login 1"
\ No newline at end of file
+    run: "ping login 1"
diff --git a/buildtest/schemas/examples/global.schema.json/invalid/maintainers_type_mismatch.yml b/buildtest/schemas/examples/global.schema.json/invalid/maintainers_type_mismatch.yml
index 20cbeb941..9fda22e9e 100644
--- a/buildtest/schemas/examples/global.schema.json/invalid/maintainers_type_mismatch.yml
+++ b/buildtest/schemas/examples/global.schema.json/invalid/maintainers_type_mismatch.yml
@@ -3,4 +3,4 @@ maintainers: 1
 buildspecs:
   hostname:
     type: script
-    run: "hostname"
\ No newline at end of file
+    run: "hostname"
diff --git a/buildtest/schemas/examples/global.schema.json/invalid/unique_maintainers.yml b/buildtest/schemas/examples/global.schema.json/invalid/unique_maintainers.yml
index c99b58036..2ed14078b 100644
--- a/buildtest/schemas/examples/global.schema.json/invalid/unique_maintainers.yml
+++ b/buildtest/schemas/examples/global.schema.json/invalid/unique_maintainers.yml
@@ -2,4 +2,4 @@ buildspecs:
   hostname:
     type: script
     run: "hostname"
-maintainers: [shahzebsiddiqui, shahzebsiddiqui]
\ No newline at end of file
+maintainers: [shahzebsiddiqui, shahzebsiddiqui]
diff --git a/buildtest/schemas/examples/global.schema.json/valid/examples.yml b/buildtest/schemas/examples/global.schema.json/valid/examples.yml
index 532aa7ff8..b1f0844cc 100644
--- a/buildtest/schemas/examples/global.schema.json/valid/examples.yml
+++ b/buildtest/schemas/examples/global.schema.json/valid/examples.yml
@@ -47,4 +47,4 @@ buildspecs:
   # testing '-' at end of word
   helloworld-:
     type: script
-    run: hostname
\ No newline at end of file
+    run: hostname
diff --git a/buildtest/schemas/examples/script.schema.json/invalid/examples.yml b/buildtest/schemas/examples/script.schema.json/invalid/examples.yml
index ebeff53a1..a1d3eb6f8 100644
--- a/buildtest/schemas/examples/script.schema.json/invalid/examples.yml
+++ b/buildtest/schemas/examples/script.schema.json/invalid/examples.yml
@@ -102,7 +102,7 @@ buildspecs:
     description: The returncode must be an int and not a number
     run: exit 1
     status:
-      returncode:  1.01
+      returncode: 1.01
 
   non_int_returncodes_list:
     type: script
@@ -110,7 +110,7 @@ buildspecs:
     description: The returncode must be a list of integers and no numbers
     run: exit 1
     status:
-      returncode:  [1, 2.230]
+      returncode: [1, 2.230]
 
   invalid_shell_type:
     type: script
@@ -151,7 +151,7 @@ buildspecs:
     type: script
     executor: generic.local.bash
     description: invalid tag value must be all string items
-    tags: ["network", 400 ]
+    tags: ["network", 400]
     run: hostname
 
   additionalProperties_test:
@@ -165,7 +165,7 @@ buildspecs:
     type: script
     executor: generic.local.bash
     description: test additional properties in status object. This is not allowed
-    sbatch: [ "-n 2", "-q normal", "-t 10"]
+    sbatch: ["-n 2", "-q normal", "-t 10"]
     run: hostname
     status:
       slurm_job_state: "COMPLETED"
@@ -271,4 +271,104 @@ buildspecs:
     description: this test will fail validation, because item must be a string
     run: mkdir -p 1
     status:
-      exists: [ 1 ]
+      exists: [1]
+
+  invalid_property_filename:
+    type: script
+    executor: generic.local.bash
+    description: Invalid property name filename under file_regex
+    run: |
+      echo "Hello" > hello.txt
+    status:
+      file_regex:
+        - filename: hello.txt
+          exp: '^(Hello)$'
+
+  testing_additionalProperties_file_regex:
+    type: script
+    executor: generic.local.bash
+    description: Testing for additional properties in file_regex
+    run: |
+      echo "Hello" > hello.txt
+    status:
+      file_regex:
+        - file: hello.txt
+          exp: '^(Hello)$'
+          FOO: BAR
+  file_count_property_count_failure:
+    type: script
+    executor: generic.local.bash
+    description: The count property in file_count must be greater than 0
+    run: hostname
+    status:
+      file_count:
+        - dir: /tmp
+          count: -1
+
+  file_count_property_depth_failure:
+    type: script
+    executor: generic.local.bash
+    description: The depth property in file_count must be greater than 0
+    run: hostname
+    status:
+      file_count:
+        - dir: /tmp
+          count: 0
+          depth: -1
+  file_count_required_property_check:
+    type: script
+    executor: generic.local.bash
+    description: The dir property in file_count is required
+    run: hostname
+    status:
+      file_count:
+        - count: 0
+  file_count_additionalProperties_check:
+    type: script
+    executor: generic.local.bash
+    description: Testing for additional properties in file_count
+    run: hostname
+    status:
+      file_count:
+        - dir: /tmp
+          count: 0
+          FOO: BAR
+    file_count_minimum_check_on_count:
+      type: script
+      executor: generic.local.bash
+      description: The count property must be 0 or greater
+      run: mkdir foo
+      status:
+        file_count:
+          - dir: foo
+            count: -1
+  file_count_filetype_invalid_value:
+    type: script
+    executor: generic.local.bash
+    description: Invalid value for filetype, it must be 'file', 'dir' or 'symlink'
+    run: mkdir foo
+    status:
+      file_count:
+        - dir: foo
+          count: -1
+          filetype: 'filename'
+  minimum_check_file_traverse_limit:
+    type: script
+    executor: generic.local.bash
+    description: The minimum value for file_traverse_limit is 1
+    run: mkdir foo
+    status:
+      file_count:
+        - dir: foo
+          count: 1
+          file_traverse_limit: 0
+  maximum_check_file_traverse_limit:
+    type: script
+    executor: generic.local.bash
+    description: The minimum value for file_traverse_limit is 999999
+    run: mkdir foo
+    status:
+      file_count:
+        - dir: foo
+          count: 1
+          file_traverse_limit: 1000000
diff --git a/buildtest/schemas/examples/script.schema.json/valid/examples.yml b/buildtest/schemas/examples/script.schema.json/valid/examples.yml
index ba47f8653..625786480 100644
--- a/buildtest/schemas/examples/script.schema.json/valid/examples.yml
+++ b/buildtest/schemas/examples/script.schema.json/valid/examples.yml
@@ -30,7 +30,7 @@ buildspecs:
     description: declaring variables
     vars:
       First: Bob
-      Last:  Bill
+      Last: Bill
     run: |
       echo "First:" $First
       echo "Last:" $Last
@@ -166,7 +166,6 @@ buildspecs:
         stream: stdout
         exp: "^hello"
 
-
   status_runtime_min_max:
     type: script
     executor: generic.local.sh
@@ -549,7 +548,7 @@ buildspecs:
     description: this test will pass
     run: mkdir -p 1
     status:
-      exists: [ '1' ]
+      exists: ['1']
 
   combined_file_and_dir_checks:
     type: script
@@ -561,4 +560,134 @@ buildspecs:
         - $HOME
         - /tmp
       is_file:
-        - $HOME/.bashrc
\ No newline at end of file
+        - $HOME/.bashrc
+
+  is_symlink_example:
+    type: script
+    executor: generic.local.bash
+    description: status check based on symbolic link
+    run: |
+        ln -s /tmp scratch
+        ln -s $HOME/.bash_profile $HOME/bash_profile
+    status:
+      is_symlink:
+        - scratch
+        - $HOME/bash_profile
+        - ~/bash_profile
+  regex_on_multiple_files:
+    type: script
+    executor: generic.local.bash
+    description: Test regex on multiple files
+    run: |
+      echo "Hello" > hello.txt
+      buildtest --help > buildtest_help.txt
+    status:
+      file_regex:
+        - file: hello.txt
+          exp: '^(Hello)$'
+        - file: buildtest_help.txt
+          exp: '^(usage: buildtest)'
+
+  regex_on_directory_not_supported:
+    type: script
+    executor: generic.local.bash
+    description: Test regex on directory is not supported
+    run: |
+      mkdir -p hello
+      echo "Hello" > hello/hello.txt
+    status:
+      file_regex:
+        - file: hello
+          exp: '^(Hello)$'
+
+  file_expansion_supported:
+    type: script
+    executor: generic.local.bash
+    description: Test regex with variable and shell expansion
+    run: |
+      echo "Hello" > $BUILDTEST_ROOT/hello.txt
+      echo "Hello" > $HOME/hello.txt
+    status:
+      file_regex:
+        - file: $BUILDTEST_ROOT/hello.txt
+          exp: '^(Hello)$'
+        - file: ~/hello.txt
+          exp: '^(Hello)$'
+
+  file_count_on_directory:
+    type: script
+    executor: generic.local.bash
+    description: file count check in directory
+    run: |
+      mkdir -p foo
+      touch foo/{1..5}
+    status:
+      file_count:
+        - dir: foo
+          count: 5
+  file_count_by_extension:
+    type: script
+    executor: generic.local.bash
+    description: file count by extension
+    run: |
+      mkdir -p foo/bar
+      touch foo/{1..5}.sh
+      touch foo/bar/{1..3}.py foo/bar/{1..3}.txt
+    status:
+      file_count:
+        - dir: foo
+          ext: '.sh'
+          depth: 1
+          count: 5
+        - dir: foo/bar
+          ext: ['.py', '.txt']
+          count: 6
+
+  file_extension_and_filepattern:
+    type: script
+    executor: generic.local.bash
+    description: file count by file extension and file pattern
+    run: |
+      touch foo{1..5}.txt
+      touch {conf,main}.py
+      ls -l
+    status:
+      file_count:
+        - dir: .
+          ext: '.txt'
+          filepattern: '(conf|main).py$'
+          count: 7
+
+  file_count_by_filetype:
+    type: script
+    executor: generic.local.bash
+    description: Count the number of directories and symbolic links
+    run: |
+      mkdir -p foo/{bar,baz}
+      find foo -type dir
+      ln -s foo/bar foo/bar.link
+      ln -s foo/baz foo/baz.link
+    status:
+      file_count:
+        - dir: foo
+          count: 3
+          filetype: 'dir'
+        - dir: foo
+          count: 2
+          filetype: 'symlink'
+
+  file_traverse_limit:
+    type: script
+    executor: generic.local.bash
+    description: Use of file_traverse_limit to limit number of files searched in a directory
+    run: |
+      mkdir foo
+      touch foo/{1..99}.txt
+    status:
+      file_count:
+        - dir: foo
+          count: 50
+          file_traverse_limit: 50
+        - dir: foo
+          count: 10
+          file_traverse_limit: 20
diff --git a/buildtest/schemas/examples/settings.schema.json/valid/cobalt-example.yml b/buildtest/schemas/examples/settings.schema.json/valid/cobalt-example.yml
index 31e8702df..c1987d517 100644
--- a/buildtest/schemas/examples/settings.schema.json/valid/cobalt-example.yml
+++ b/buildtest/schemas/examples/settings.schema.json/valid/cobalt-example.yml
@@ -5,7 +5,7 @@ system:
     poolsize: 1
     executors:
       defaults:
-         maxpendtime: 30
+        maxpendtime: 30
       local:
         bash:
           description: submit jobs via bash shell
@@ -23,4 +23,4 @@ system:
           default:
             cc: /usr/bin/gcc
             cxx: /usr/bin/g++
-            fc: /usr/bin/gfortran
\ No newline at end of file
+            fc: /usr/bin/gfortran
diff --git a/buildtest/schemas/examples/settings.schema.json/valid/combined_executor.yml b/buildtest/schemas/examples/settings.schema.json/valid/combined_executor.yml
index 9cd64b72a..8a6b44fa8 100644
--- a/buildtest/schemas/examples/settings.schema.json/valid/combined_executor.yml
+++ b/buildtest/schemas/examples/settings.schema.json/valid/combined_executor.yml
@@ -28,4 +28,4 @@ system:
           default:
             cc: /usr/bin/gcc
             cxx: /usr/bin/g++
-            fc: /usr/bin/gfortran
\ No newline at end of file
+            fc: /usr/bin/gfortran
diff --git a/buildtest/schemas/examples/settings.schema.json/valid/lsf-example.yml b/buildtest/schemas/examples/settings.schema.json/valid/lsf-example.yml
index a6853ea43..2af44bd29 100644
--- a/buildtest/schemas/examples/settings.schema.json/valid/lsf-example.yml
+++ b/buildtest/schemas/examples/settings.schema.json/valid/lsf-example.yml
@@ -32,4 +32,4 @@ system:
           default:
             cc: /usr/bin/gcc
             cxx: /usr/bin/g++
-            fc: /usr/bin/gfortran
\ No newline at end of file
+            fc: /usr/bin/gfortran
diff --git a/buildtest/schemas/examples/settings.schema.json/valid/pbs-example.yml b/buildtest/schemas/examples/settings.schema.json/valid/pbs-example.yml
index 5c6ffa29e..583a5e2b1 100644
--- a/buildtest/schemas/examples/settings.schema.json/valid/pbs-example.yml
+++ b/buildtest/schemas/examples/settings.schema.json/valid/pbs-example.yml
@@ -5,8 +5,8 @@ system:
     poolsize: 1
     executors:
       defaults:
-         pollinterval: 10
-         maxpendtime: 30
+        pollinterval: 10
+        maxpendtime: 30
       local:
         bash:
           description: submit jobs via bash shell
@@ -20,4 +20,4 @@ system:
           default:
             cc: /usr/bin/gcc
             cxx: /usr/bin/g++
-            fc: /usr/bin/gfortran
\ No newline at end of file
+            fc: /usr/bin/gfortran
diff --git a/buildtest/schemas/examples/settings.schema.json/valid/slurm-example.yml b/buildtest/schemas/examples/settings.schema.json/valid/slurm-example.yml
index 59b739980..1ea2af36b 100644
--- a/buildtest/schemas/examples/settings.schema.json/valid/slurm-example.yml
+++ b/buildtest/schemas/examples/settings.schema.json/valid/slurm-example.yml
@@ -28,4 +28,4 @@ system:
           default:
             cc: /usr/bin/gcc
             cxx: /usr/bin/g++
-            fc: /usr/bin/gfortran
\ No newline at end of file
+            fc: /usr/bin/gfortran
diff --git a/buildtest/schemas/examples/spack.schema.json/invalid/examples.yml b/buildtest/schemas/examples/spack.schema.json/invalid/examples.yml
index f0a5d8187..2bc0df56d 100644
--- a/buildtest/schemas/examples/spack.schema.json/invalid/examples.yml
+++ b/buildtest/schemas/examples/spack.schema.json/invalid/examples.yml
@@ -50,7 +50,7 @@ buildspecs:
       root: /tmp/spack
       verify_spack: false
       install:
-        specs: ['m4', 'zlib' ]
+        specs: ['m4', 'zlib']
       test:
         FOO: BAR
         remove_tests: true
@@ -69,7 +69,7 @@ buildspecs:
       root: /tmp/spack
       verify_spack: false
       install:
-        specs: ['m4', 'zlib' ]
+        specs: ['m4', 'zlib']
       test:
         remove_tests: true
         run:
@@ -92,7 +92,7 @@ buildspecs:
       root: /tmp/spack
       verify_spack: false
       install:
-        specs: ['m4', 'zlib' ]
+        specs: ['m4', 'zlib']
       test:
         remove_tests: true
         run:
@@ -104,4 +104,4 @@ buildspecs:
 
     post_cmds: |
       spack find
-      rm -rf $SPACK_ROOT
\ No newline at end of file
+      rm -rf $SPACK_ROOT
diff --git a/buildtest/schemas/examples/spack.schema.json/valid/examples.yml b/buildtest/schemas/examples/spack.schema.json/valid/examples.yml
index 2db31c1f9..d33e94fc4 100644
--- a/buildtest/schemas/examples/spack.schema.json/valid/examples.yml
+++ b/buildtest/schemas/examples/spack.schema.json/valid/examples.yml
@@ -156,7 +156,7 @@ buildspecs:
       root: /tmp/spack
       verify_spack: false
       install:
-        specs: ['m4', 'zlib' ]
+        specs: ['m4', 'zlib']
       test:
         remove_tests: true
         run:
@@ -244,4 +244,4 @@ buildspecs:
           - 'm4'
         activate:
           name: m4
-        concretize: true
\ No newline at end of file
+        concretize: true
diff --git a/buildtest/system.py b/buildtest/system.py
index e92c2aabb..2921c3c0b 100644
--- a/buildtest/system.py
+++ b/buildtest/system.py
@@ -169,7 +169,6 @@ class Slurm(Scheduler):
     binaries = ["sbatch", "sacct", "sacctmgr", "sinfo", "scancel"]
 
     def __init__(self):
-
         self.logger = logging.getLogger(__name__)
 
         self.state = self.check_binaries(self.binaries)
@@ -182,7 +181,8 @@ def __init__(self):
 
     def active(self):
         """Slurm scheduler is active if we are able to retrieve partitions or qos from scheduler. This method
-        will return a boolean type where ``True`` indicates that slurm executors can be validated."""
+        will return a boolean type where ``True`` indicates that slurm executors can be validated.
+        """
 
         return hasattr(self, "partitions") or hasattr(self, "qos")
 
@@ -264,7 +264,6 @@ class LSF(Scheduler):
     binaries = ["bsub", "bqueues", "bkill", "bjobs"]
 
     def __init__(self):
-
         self.logger = logging.getLogger(__name__)
 
         self.state = self.check_binaries(self.binaries)
@@ -365,7 +364,6 @@ def __init__(self):
             self.queues = self._get_queues()
 
     def check(self, binaries):
-
         binary_validation = super().check_binaries(binaries)
 
         if not binary_validation:
diff --git a/buildtest/tools/stylecheck.py b/buildtest/tools/stylecheck.py
index 1e17340df..1abe5e621 100644
--- a/buildtest/tools/stylecheck.py
+++ b/buildtest/tools/stylecheck.py
@@ -5,6 +5,26 @@
 from buildtest.utils.command import BuildTestCommand
 
 
+def run_command(cmd, msg):
+    """This method is a wrapper to BuildTestCommand used with running black, isort, and pyflakes during style check
+
+    Args:
+        cmd (str): Name of command to run
+        msg (str): Message printed during style check
+    """
+    console.print(f"{msg}: {cmd}")
+    result = BuildTestCommand(cmd)
+    out, err = result.execute()
+    if result.returncode() == 0:
+        console.print(f"[green]{msg} PASSED")
+    else:
+        console.print(f"[red]{msg} FAILED")
+    console.rule(f"{msg} output message")
+    console.print("".join(out))
+    console.rule(f"{msg} error message")
+    console.print("".join(err))
+
+
 def run_black(source_files, black_opts):
     """This method will run `black <https://black.readthedocs.io/>`_ check given a set of source files and black options.
     If black is not available we will return immediately otherwise we run black checks and print output and error message
@@ -18,22 +38,8 @@ def run_black(source_files, black_opts):
     if not shutil.which("black"):
         return
 
-    source_files = " ".join(source_files)
-    black_cmd = f"black {black_opts} {source_files}"
-    console.print(f"Running black check: {black_cmd}")
-    cmd = BuildTestCommand(black_cmd)
-    out, err = cmd.execute()
-
-    if cmd.returncode() == 0:
-        console.print("[green]black style check PASSED")
-    else:
-        console.print("[red]black style check FAILED")
-
-    console.rule("black output message")
-    console.print("".join(out))
-
-    console.rule("black error message")
-    console.print("".join(err))
+    cmd = f"black {black_opts} {' '.join(source_files)}"
+    run_command(cmd, "Running black check")
 
 
 def run_isort(source_files, isort_opts):
@@ -48,24 +54,9 @@ def run_isort(source_files, isort_opts):
     if not shutil.which("isort"):
         return
 
-    source_files = " ".join(source_files)
-
-    isort_cmd = f"isort --settings-path {os.path.join(BUILDTEST_ROOT, '.isort.cfg')} {isort_opts} {source_files}"
-    console.print(f"Running isort check: {isort_cmd}")
-
-    cmd = BuildTestCommand(isort_cmd)
-    out, err = cmd.execute()
-
-    if cmd.returncode() == 0:
-        console.print("[green]isort style check PASSED")
-    else:
-        console.print("[red]Black style check FAILED")
-
-    console.rule("isort output message")
-    console.print("".join(out))
-
-    console.rule("isort error message")
-    console.print("".join(err))
+    # source_files = " ".join(source_files)
+    cmd = f"isort --settings-path {os.path.join(BUILDTEST_ROOT, '.isort.cfg')} {isort_opts} {' '.join(source_files)}"
+    run_command(cmd, "Running isort check")
 
 
 def run_pyflakes(source_files):
@@ -79,24 +70,8 @@ def run_pyflakes(source_files):
     if not shutil.which("pyflakes"):
         return
 
-    source_files = " ".join(source_files)
-
-    pyflakes_cmd = f"pyflakes {source_files}"
-    console.print(f"Running pyflakes check: {pyflakes_cmd}")
-
-    cmd = BuildTestCommand(pyflakes_cmd)
-    out, err = cmd.execute()
-
-    if cmd.returncode() == 0:
-        console.print("[green]pyflakes style check PASSED")
-    else:
-        console.print("[red]pyflakes style check FAILED")
-
-    console.rule("pyflakes output message")
-    console.print("".join(out))
-
-    console.rule("pyflakes error message")
-    console.print("".join(err))
+    cmd = f"pyflakes {' '.join(source_files)}"
+    run_command(cmd, "Running pyflakes check")
 
 
 def run_style_checks(no_black, no_isort, no_pyflakes, apply_stylechecks):
diff --git a/buildtest/tools/tutorialexamples.py b/buildtest/tools/tutorialexamples.py
index 84f598361..59e9f1c15 100644
--- a/buildtest/tools/tutorialexamples.py
+++ b/buildtest/tools/tutorialexamples.py
@@ -42,5 +42,4 @@ def generate_tutorial_examples():
 
 
 if __name__ == "__main__":
-
     generate_tutorial_examples()
diff --git a/buildtest/tools/unittests.py b/buildtest/tools/unittests.py
index 509c77a8f..9101fe091 100644
--- a/buildtest/tools/unittests.py
+++ b/buildtest/tools/unittests.py
@@ -72,6 +72,7 @@ def run_unit_tests(pytestopts=None, sourcefiles=None, enable_coverage=False):
 
     if enable_coverage:
         cov.stop()
+        cov.combine()
         cov.html_report(title="buildtest unittests coverage report", directory=html_dir)
         cov.json_report(outfile=os.path.join(BUILDTEST_ROOT, "coverage.json"))
         cov.report(ignore_errors=True, skip_empty=True, sort="-cover", precision=2)
@@ -84,7 +85,6 @@ def run_unit_tests(pytestopts=None, sourcefiles=None, enable_coverage=False):
 
 
 if __name__ == "__main__":
-
     parser = argparse.ArgumentParser(
         prog="unittest",
         description="Run buildtest unit tests",
diff --git a/buildtest/utils/file.py b/buildtest/utils/file.py
index 1663c6def..10a27ee2e 100644
--- a/buildtest/utils/file.py
+++ b/buildtest/utils/file.py
@@ -10,9 +10,13 @@
 
 import json
 import os
+import re
 
+from buildtest.defaults import console
 from buildtest.exceptions import BuildTestError
 
+max_files_traversed = 999999
+
 
 def is_file(fname):
     """Check if file exist and returns True/False
@@ -57,35 +61,199 @@ def is_dir(dirname):
     return os.path.isdir(dirname)
 
 
-def walk_tree(root_dir, ext=None):
+def is_symlink(filename):
+    """Check if the given link is a symlink and return ``True``, if it is not a symlink or the symlink is broken return ``False``.
+
+    Args:
+        filename (str): Input filename to check if its a symbolic link
+
+    Returns:
+        bool: True if filename is a symbolic link otherwise return False if its not a symbolic link or broken link
+    """
+
+    # apply shell expansion  when file includes something like $HOME/example
+    expanded_filepath = os.path.expandvars(filename)
+
+    # apply user expansion when file includes something like  ~/example
+    expanded_filepath = os.path.expanduser(expanded_filepath)
+
+    # resolve path will return canonical path eliminating any symbolic links encountered
+    resolved_sym_link = resolve_path(filename)
+
+    return os.path.islink(expanded_filepath) and resolved_sym_link
+
+
+def search_files(
+    root_dir,
+    regex_pattern,
+    numfiles=None,
+    max_depth=None,
+    file_traverse_limit=999999,
+    file_type="file",
+):
+    """
+    This method will search for files in a directory based on a regex pattern.
+
+    Args:
+        root_dir (str): Root directory to search for files
+        regex_pattern (str): A regex pattern to search for files
+        numfiles (int, optional): Specify number of files to return. If not specified, all files will be returned.
+        max_depth (int, optional): Specify maximum depth to traverse during directory walk.
+        file_traverse_limit (int, optional): Maximum number of files to traverse during directory walk
+        file_type (str, optional): Type of files to return, either 'file', 'dir', or 'symlink'. Default is 'file'.
+
+    Returns: A list of files that match the regex pattern
+
+    """
+    files_list = []
+    files_traversed = 0
+
+    # if file_traverse_limit is None then we set it to max_files_traversed
+    file_traverse_limit = file_traverse_limit or max_files_traversed
+
+    # if user specified file_traverse_limit take the minimum of file_traverse_limit and max_files_traversed
+    file_traverse_limit = min(file_traverse_limit, max_files_traversed)
+    # if file_type is None then we set it to 'file' by default
+    file_type = file_type or "file"
+
+    # re.compile can raise exception if regex pattern is not valid which will raise re.error
+    try:
+        pattern = re.compile(regex_pattern)
+    except re.error as err:
+        print(err)
+        console.print(
+            f"Unable to compile regular expression: {regex_pattern}, please try again",
+            style="bold red",
+        )
+        return files_list
+
+    if not is_dir(root_dir):
+        return files_list
+
+    resolved_dirpath = resolve_path(root_dir, exist=True)
+
+    for root, dirs, files in os.walk(resolved_dirpath):
+        if (
+            max_depth is not None
+            and root.count(os.sep) - resolved_dirpath.count(os.sep) >= max_depth
+        ):
+            del dirs[:]
+            continue
+
+        # by default 'root' is a directory during os.walk so we just append it to list
+        if file_type == "dir":
+            files_list.append(root)
+
+        for file in files:
+            # if we have reached the file traverse limit then we return list of files
+            if files_traversed >= file_traverse_limit:
+                return [os.path.abspath(fname) for fname in files_list]
+
+            # if numfiles is specified then we return list of files once we reach the number of files
+            if numfiles is not None:
+                if len(files_list) >= numfiles:
+                    return [os.path.abspath(fname) for fname in files_list]
+
+            file_path = os.path.join(root, file)
+            if file_type == "file" and pattern.search(file):
+                files_list.append(file_path)
+
+            elif (
+                file_type == "symlink"
+                and is_symlink(file_path)
+                and pattern.search(file)
+            ):
+                files_list.append(file_path)
+
+            files_traversed += 1
+
+    return [os.path.abspath(fname) for fname in files_list]
+
+
+def walk_tree(
+    root_dir,
+    ext=None,
+    max_depth=None,
+    numfiles=None,
+    file_traverse_limit=999999,
+    file_type="file",
+):
     """This method will traverse a directory tree and return list of files
     based on extension type. This method invokes :func:`is_dir` to check if directory
     exists before traversal.
 
     Args:
         root_dir (str): directory path to traverse
-        ext (str): File extension to search in traversal
+        ext (str or list, optional): File extension or list of file extensions to search in traversal
+        max_depth (int, optional): Maximum depth to traverse
+        numfiles (int, optional): Number of files to return
+        file_traverse_limit (int, optional): Maximum number of files to traverse during directory walk
+        file_type (str, optional): Type of files to return, either 'file', 'dir', or 'symlink'. Default is 'file'.
+
 
     Returns:
         list: A list of file paths for a directory traversal based on extension type. If ``ext`` is **None** we retrieve all files
     """
 
-    list_files = []
-    # if directory doesn't exist let's return empty list before doing a directory traversal since no files to traverse
+    files_list = []
     if not is_dir(root_dir):
-        return list_files
+        return files_list
+
+    if isinstance(ext, str):
+        ext = [ext]
+
+    # if file_type is None then we set it to 'file' by default
+    file_type = file_type or "file"
+
+    resolved_dirpath = resolve_path(root_dir, exist=True)
+    files_traversed = 0
 
-    for root, subdir, files in os.walk(root_dir):
-        for fname in files:
-            # if ext is provided check if file ends with extension and add to list, otherwise
-            # add all files to list and return
-            if ext:
-                if fname.endswith(ext):
-                    list_files.append(os.path.join(root, fname))
-            else:
-                list_files.append(os.path.join(root, fname))
+    # if file_traverse_limit is None then we set it to max_files_traversed
+    file_traverse_limit = file_traverse_limit or max_files_traversed
 
-    return [os.path.abspath(fname) for fname in list_files]
+    # user may pass a file_traverse_limit that is too large, so we set a limit to 999999
+    file_traverse_limit = min(file_traverse_limit, max_files_traversed)
+
+    for root, dirs, files in os.walk(resolved_dirpath):
+        if (
+            max_depth is not None
+            and root.count(os.sep) - resolved_dirpath.count(os.sep) >= max_depth
+        ):
+            del dirs[:]
+            continue
+
+        # by default 'root' is a directory during os.walk so we just append it to list
+        if file_type == "dir":
+            files_list.append(root)
+
+        for file in files:
+            # if we have reached the file traverse limit then we return list of files
+            if files_traversed >= file_traverse_limit:
+                return [os.path.abspath(fname) for fname in files_list]
+
+            # if numfiles is specified then we return list of files once we reach the number of files
+            if numfiles is not None:
+                if len(files_list) >= numfiles:
+                    return [os.path.abspath(fname) for fname in files_list]
+
+            file_path = os.path.join(root, file)
+
+            # if file_type is 'file' and extension matches then we append file to list. If extension is None the statement is True.
+            if file_type == "file" and (
+                ext is None or os.path.splitext(file)[1] in ext
+            ):
+                files_list.append(file_path)
+            # if file_type is 'symlink' and file is a symlink and extension matches then we append file to list. If extension is None the statement is True.
+            elif (
+                file_type == "symlink"
+                and os.path.islink(file_path)
+                and (ext is None or os.path.splitext(file)[1] in ext)
+            ):
+                files_list.append(file_path)
+
+            files_traversed += 1
+
+    return [os.path.abspath(fname) for fname in files_list]
 
 
 def create_dir(dirname):
@@ -199,6 +367,11 @@ def read_file(filepath):
     return content
 
 
+def create_file(filepath):
+    """This method will create an empty file"""
+    write_file(filepath, content="")
+
+
 def write_file(filepath, content):
     """This method is used to write an input ``content`` to a file specified by
     ``filepath``. Both filepath and content must be a str. An error is raised
diff --git a/buildtest/utils/shell.py b/buildtest/utils/shell.py
index d61c0cd73..55ff7cf8e 100755
--- a/buildtest/utils/shell.py
+++ b/buildtest/utils/shell.py
@@ -79,7 +79,6 @@ def shell_lookup():
     }
 
     for name in shells.keys():
-
         cmd = BuildTestCommand(f"which -a {name}")
         cmd.execute()
         out = cmd.get_output()
diff --git a/docs/gettingstarted/buildingtest.rst b/docs/gettingstarted/buildingtest.rst
index 1825a314c..d05e3b1c6 100644
--- a/docs/gettingstarted/buildingtest.rst
+++ b/docs/gettingstarted/buildingtest.rst
@@ -85,6 +85,7 @@ by ``-x`` option. You can specify ``-x`` multiple times just like ``-b`` option.
 For example, we can undo discovery by passing same option to ``-b`` and ``-x``  as follows
 
 .. dropdown:: ``buildtest bd -b tutorials/ -x tutorials/``
+   :color: warning
 
     .. command-output:: buildtest bd -b tutorials/ -x tutorials/
         :returncode: 1
@@ -332,6 +333,38 @@ If you try to exceed this bound you will get an error such as
     .. command-output:: buildtest build -b tutorials/test_status/pass_returncode.yml --rebuild 51
         :returncode: 1
 
+Limit Number of Tests
+----------------------
+
+The `buildtest build` command can limit the number of tests that can run via ``--limit`` option. This
+can be useful when running large number of tests and you have no idea
+how many tests will run. The ``--limit <NUM>`` option expects a positive number which will
+limit number of tests to the total limit. If there are less tests to run than the
+value specified by ``--limit``, then buildtest will run all the test. When buildtest has more
+tests to run than the value specified by ``--limit``, then buildtest will exclude some tests.
+
+To demonstrate this feature, we will run the same command with and without **--limit** option.
+
+In first example, we will run a test that will run 4 tests.
+
+.. dropdown:: ``buildtest build -b tutorials/test_status/pass_returncode.yml``
+
+    .. command-output:: buildtest build -b tutorials/test_status/pass_returncode.yml
+
+Now let's run this same test with ``--limit=2`` and notice buildtest will run **2/4** tests
+
+.. dropdown:: ``buildtest build -b tutorials/test_status/pass_returncode.yml --limit=2``
+
+    .. command-output:: buildtest build -b tutorials/test_status/pass_returncode.yml --limit=2
+
+If you specify 0 or negative number you will get an error as follows
+
+.. dropdown:: ``buildtest build -b tutorials/test_status/pass_returncode.yml --limit=0``
+    :color: warning
+
+    .. command-output:: buildtest build -b tutorials/test_status/pass_returncode.yml --limit=0
+        :returncode: 2
+
 Rerun Last command
 -------------------
 
diff --git a/scripts/spack_container/regtest-tutorial.sh b/scripts/spack_container/regtest-tutorial.sh
index 07b1c1a3d..3fb4bb3c1 100755
--- a/scripts/spack_container/regtest-tutorial.sh
+++ b/scripts/spack_container/regtest-tutorial.sh
@@ -2,5 +2,5 @@
 . /etc/profile
 export PATH=/home/spack/.local/bin:$PATH
 . /home/spack/buildtest/scripts/spack_container/setup.sh
-pip3 install -r $BUILDTEST_ROOT/docs/requirements.txt &> /dev/null
-python $BUILDTEST_ROOT/buildtest/tools/unittests.py
\ No newline at end of file
+pip3 install -r "${BUILDTEST_ROOT}/docs/requirements.txt" &> /dev/null
+python "${BUILDTEST_ROOT}/buildtest/tools/unittests.py"
\ No newline at end of file
diff --git a/tests/builders/assert_lt.yml b/tests/builders/assert_lt.yml
new file mode 120000
index 000000000..a68c51e33
--- /dev/null
+++ b/tests/builders/assert_lt.yml
@@ -0,0 +1,1 @@
+../../tutorials/perf_checks/assert_lt.yml
\ No newline at end of file
diff --git a/tests/builders/file_count.yml b/tests/builders/file_count.yml
new file mode 120000
index 000000000..e720cfae2
--- /dev/null
+++ b/tests/builders/file_count.yml
@@ -0,0 +1,1 @@
+../../tutorials/test_status/file_count.yml
\ No newline at end of file
diff --git a/tests/builders/file_count_file_traverse_limit.yml b/tests/builders/file_count_file_traverse_limit.yml
new file mode 120000
index 000000000..1d5004a92
--- /dev/null
+++ b/tests/builders/file_count_file_traverse_limit.yml
@@ -0,0 +1,1 @@
+../../tutorials/test_status/file_count_file_traverse_limit.yml
\ No newline at end of file
diff --git a/tests/builders/file_count_filetype.yml b/tests/builders/file_count_filetype.yml
new file mode 120000
index 000000000..348ae401c
--- /dev/null
+++ b/tests/builders/file_count_filetype.yml
@@ -0,0 +1,1 @@
+../../tutorials/test_status/file_count_filetype.yml
\ No newline at end of file
diff --git a/tests/builders/file_count_pattern.yml b/tests/builders/file_count_pattern.yml
new file mode 120000
index 000000000..94382076f
--- /dev/null
+++ b/tests/builders/file_count_pattern.yml
@@ -0,0 +1,1 @@
+../../tutorials/test_status/file_count_pattern.yml
\ No newline at end of file
diff --git a/tests/builders/is_symlink.yml b/tests/builders/is_symlink.yml
new file mode 120000
index 000000000..1c82585df
--- /dev/null
+++ b/tests/builders/is_symlink.yml
@@ -0,0 +1,1 @@
+../../tutorials/test_status/is_symlink.yml
\ No newline at end of file
diff --git a/tests/builders/regex_on_filename.yml b/tests/builders/regex_on_filename.yml
new file mode 120000
index 000000000..a66d37380
--- /dev/null
+++ b/tests/builders/regex_on_filename.yml
@@ -0,0 +1,1 @@
+../../tutorials/test_status/regex_on_filename.yml
\ No newline at end of file
diff --git a/tests/builders/test_builders.py b/tests/builders/test_builders.py
index ac044d4cc..598933e10 100644
--- a/tests/builders/test_builders.py
+++ b/tests/builders/test_builders.py
@@ -14,7 +14,7 @@
 
 
 def test_assert_ge():
-
+    """This test buildspec using status check with  'assert_ge'"""
     cmd = BuildTest(
         buildspecs=[os.path.join(here, "assert_ge.yml")],
         buildtest_system=system,
@@ -24,7 +24,7 @@ def test_assert_ge():
 
 
 def test_assert_le():
-
+    """This test buildspec using status check with  'assert_le'"""
     cmd = BuildTest(
         buildspecs=[os.path.join(here, "assert_le.yml")],
         buildtest_system=system,
@@ -34,7 +34,7 @@ def test_assert_le():
 
 
 def test_assert_gt():
-
+    """This test buildspec using status check with  'assert_gt'"""
     cmd = BuildTest(
         buildspecs=[os.path.join(here, "assert_gt.yml")],
         buildtest_system=system,
@@ -43,7 +43,18 @@ def test_assert_gt():
     cmd.build()
 
 
+def test_assert_lt():
+    """This test buildspec using status check with  'assert_lt'"""
+    cmd = BuildTest(
+        buildspecs=[os.path.join(here, "assert_lt.yml")],
+        buildtest_system=system,
+        configuration=config,
+    )
+    cmd.build()
+
+
 def test_assert_eq():
+    """This test buildspec using status check with  'assert_eq'"""
     cmd = BuildTest(
         buildspecs=[os.path.join(here, "assert_eq.yml")],
         buildtest_system=system,
@@ -53,6 +64,7 @@ def test_assert_eq():
 
 
 def test_assert_ne():
+    """This test buildspec using status check with  'assert_ne'"""
     cmd = BuildTest(
         buildspecs=[os.path.join(here, "assert_ne.yml")],
         buildtest_system=system,
@@ -62,6 +74,8 @@ def test_assert_ne():
 
 
 def test_assert_contains():
+    """This test buildspec using status check with  'assert_contains'"""
+
     cmd = BuildTest(
         buildspecs=[os.path.join(here, "contains.yml")],
         buildtest_system=system,
@@ -71,6 +85,8 @@ def test_assert_contains():
 
 
 def test_assert_range():
+    """This test buildspec using status check with  'assert_range'"""
+
     cmd = BuildTest(
         buildspecs=[os.path.join(here, "assert_range.yml")],
         buildtest_system=system,
@@ -79,7 +95,18 @@ def test_assert_range():
     cmd.build()
 
 
+def test_assert_is_symlink():
+    """This test buildspec using status check with  'is_symlink'"""
+    cmd = BuildTest(
+        buildspecs=[os.path.join(here, "is_symlink.yml")],
+        buildtest_system=system,
+        configuration=config,
+    )
+    cmd.build()
+
+
 def test_assert_exists():
+    """This test buildspec using status check with  'exists'"""
     cmd = BuildTest(
         buildspecs=[os.path.join(here, "exists.yml")],
         buildtest_system=system,
@@ -89,9 +116,25 @@ def test_assert_exists():
 
 
 def test_assert_is_file_is_dir():
+    """This test buildspec using status check with  'is_dir' and 'is_file'"""
     cmd = BuildTest(
         buildspecs=[os.path.join(here, "file_and_dir_check.yml")],
         buildtest_system=system,
         configuration=config,
     )
     cmd.build()
+
+
+def test_file_count():
+    """This test buildspec using status check with  'file_count'"""
+    cmd = BuildTest(
+        buildspecs=[
+            os.path.join(here, "file_count.yml"),
+            os.path.join(here, "file_count_pattern.yml"),
+            os.path.join(here, "file_count_filetype.yml"),
+            os.path.join(here, "file_count_file_traverse_limit.yml"),
+        ],
+        buildtest_system=system,
+        configuration=config,
+    )
+    cmd.build()
diff --git a/tests/buildsystem/invalid_builds/invalid_file.yml b/tests/buildsystem/invalid_builds/invalid_file.yml
index a82d6ddf7..4f5b76955 100644
--- a/tests/buildsystem/invalid_builds/invalid_file.yml
+++ b/tests/buildsystem/invalid_builds/invalid_file.yml
@@ -9,4 +9,3 @@ buildspecs:
       default:
         gcc:
           cflags: -O3
-
diff --git a/tests/buildsystem/invalid_builds/invalid_lang_ext.yml b/tests/buildsystem/invalid_builds/invalid_lang_ext.yml
index 29ff7a164..516fab0b1 100644
--- a/tests/buildsystem/invalid_builds/invalid_lang_ext.yml
+++ b/tests/buildsystem/invalid_builds/invalid_lang_ext.yml
@@ -8,4 +8,4 @@ buildspecs:
       name: ["^(builtin_gcc)$"]
       default:
         gcc:
-          cflags: -O3
\ No newline at end of file
+          cflags: -O3
diff --git a/tests/buildsystem/invalid_buildspecs/invalid_executor.yml b/tests/buildsystem/invalid_buildspecs/invalid_executor.yml
index 7163d628a..87c91f172 100644
--- a/tests/buildsystem/invalid_buildspecs/invalid_executor.yml
+++ b/tests/buildsystem/invalid_buildspecs/invalid_executor.yml
@@ -2,4 +2,4 @@ buildspecs:
   invalid_executor:
     executor: not_valid_executor_name
     type: script
-    run: hostname
\ No newline at end of file
+    run: hostname
diff --git a/tests/buildsystem/invalid_buildspecs/invalid_type.yml b/tests/buildsystem/invalid_buildspecs/invalid_type.yml
index b18245363..8e91598f6 100644
--- a/tests/buildsystem/invalid_buildspecs/invalid_type.yml
+++ b/tests/buildsystem/invalid_buildspecs/invalid_type.yml
@@ -2,4 +2,4 @@ buildspecs:
   type_mismatch:
     executor: generic.local.sh
     type: badvalue
-    run: hostname
\ No newline at end of file
+    run: hostname
diff --git a/tests/buildsystem/invalid_buildspecs/missing_executor.yml b/tests/buildsystem/invalid_buildspecs/missing_executor.yml
index f84034417..2ba8f4d94 100644
--- a/tests/buildsystem/invalid_buildspecs/missing_executor.yml
+++ b/tests/buildsystem/invalid_buildspecs/missing_executor.yml
@@ -1,4 +1,4 @@
 buildspecs:
   missing_executor:
     type: script
-    run: hostname
\ No newline at end of file
+    run: hostname
diff --git a/tests/buildsystem/invalid_buildspecs/missing_type.yml b/tests/buildsystem/invalid_buildspecs/missing_type.yml
index 886b26152..2e7f8de05 100644
--- a/tests/buildsystem/invalid_buildspecs/missing_type.yml
+++ b/tests/buildsystem/invalid_buildspecs/missing_type.yml
@@ -1,4 +1,4 @@
 buildspecs:
   missing_type:
     executor: generic.local.sh
-    run: hostname
\ No newline at end of file
+    run: hostname
diff --git a/tests/buildsystem/test_base.py b/tests/buildsystem/test_base.py
index 9b9507025..d055ce40d 100644
--- a/tests/buildsystem/test_base.py
+++ b/tests/buildsystem/test_base.py
@@ -143,7 +143,6 @@ def test_BuildspecParser(tmp_path):
         assert builders
 
         for builder in builders:
-
             # Builders (on init) set up metadata attribute
             assert hasattr(builder, "metadata")
 
diff --git a/tests/buildsystem/test_spack.py b/tests/buildsystem/test_spack.py
index ce705280c..9ee4cf31c 100644
--- a/tests/buildsystem/test_spack.py
+++ b/tests/buildsystem/test_spack.py
@@ -10,7 +10,6 @@
 
 
 def test_spack_examples():
-
     # spack builds must run in container  ghcr.io/buildtesters/buildtest_spack:latest which comes with username 'spack' and home directory '/home/spack'
     if not (getpass.getuser() == "spack" and os.path.expanduser("~") == "/home/spack"):
         pytest.skip(
diff --git a/tests/buildsystem/valid_builds/sched_directives.yml b/tests/buildsystem/valid_builds/sched_directives.yml
index cc16f6e38..914047949 100644
--- a/tests/buildsystem/valid_builds/sched_directives.yml
+++ b/tests/buildsystem/valid_builds/sched_directives.yml
@@ -25,4 +25,4 @@ buildspecs:
     executor: generic.local.bash
     description: PBS batch generation check
     cobalt: ["-l nodes=1", "-l walltime=10"]
-    run: hostname
\ No newline at end of file
+    run: hostname
diff --git a/tests/buildsystem/valid_buildspecs/gnu_hello_c.yml b/tests/buildsystem/valid_buildspecs/gnu_hello_c.yml
index 55790593c..c207f1e12 100644
--- a/tests/buildsystem/valid_buildspecs/gnu_hello_c.yml
+++ b/tests/buildsystem/valid_buildspecs/gnu_hello_c.yml
@@ -12,4 +12,4 @@ buildspecs:
           cflags: -O1
       config:
         builtin_gcc:
-          cflags: -O2
\ No newline at end of file
+          cflags: -O2
diff --git a/tests/buildsystem/valid_buildspecs/pre_post_build_run.yml b/tests/buildsystem/valid_buildspecs/pre_post_build_run.yml
index 5ef4a555c..a27b6afad 100644
--- a/tests/buildsystem/valid_buildspecs/pre_post_build_run.yml
+++ b/tests/buildsystem/valid_buildspecs/pre_post_build_run.yml
@@ -20,4 +20,4 @@ buildspecs:
             echo "This is pre-run section"
             export FOO=BAR
           post_run: |
-            echo "This is post-run section"
\ No newline at end of file
+            echo "This is post-run section"
diff --git a/tests/buildsystem/valid_buildspecs/shell_examples.yml b/tests/buildsystem/valid_buildspecs/shell_examples.yml
index e4807f908..760c8ad33 100644
--- a/tests/buildsystem/valid_buildspecs/shell_examples.yml
+++ b/tests/buildsystem/valid_buildspecs/shell_examples.yml
@@ -34,4 +34,4 @@ buildspecs:
     shell: "sh -x"
     run: |
       echo $SHELL
-      hostname
\ No newline at end of file
+      hostname
diff --git a/tests/cli/cdash_examples/invalid_project.yml b/tests/cli/cdash_examples/invalid_project.yml
index 53feac53b..2cb4f8876 100644
--- a/tests/cli/cdash_examples/invalid_project.yml
+++ b/tests/cli/cdash_examples/invalid_project.yml
@@ -6,7 +6,7 @@ system:
     moduletool: N/A
     cdash:
       url: "https://my.cdash.org"
-      project:  INVALID-PROJECT
+      project: INVALID-PROJECT
       site: laptop
     executors:
       local:
@@ -33,4 +33,4 @@ system:
           builtin_gcc:
             cc: /usr/bin/gcc
             fc: /usr/bin/gfortran
-            cxx: /usr/bin/g++
\ No newline at end of file
+            cxx: /usr/bin/g++
diff --git a/tests/cli/cdash_examples/invalid_url.yml b/tests/cli/cdash_examples/invalid_url.yml
index c6dc23a0c..138dac285 100644
--- a/tests/cli/cdash_examples/invalid_url.yml
+++ b/tests/cli/cdash_examples/invalid_url.yml
@@ -6,7 +6,7 @@ system:
     moduletool: N/A
     cdash:
       url: "https://my.cdash.XYZ.org"
-      project:  buildtest
+      project: buildtest
       site: laptop
     executors:
       local:
@@ -33,4 +33,4 @@ system:
           builtin_gcc:
             cc: /usr/bin/gcc
             fc: /usr/bin/gfortran
-            cxx: /usr/bin/g++
\ No newline at end of file
+            cxx: /usr/bin/g++
diff --git a/tests/cli/configuration/invalid_executors.yml b/tests/cli/configuration/invalid_executors.yml
index 70440ee1e..dcffa86e1 100644
--- a/tests/cli/configuration/invalid_executors.yml
+++ b/tests/cli/configuration/invalid_executors.yml
@@ -30,4 +30,4 @@ system:
           builtin_gcc:
             cc: gcc
             fc: gfortran
-            cxx: g++
\ No newline at end of file
+            cxx: g++
diff --git a/tests/cli/test_argparse.py b/tests/cli/test_argparse.py
index 464d4f9cf..cbfb9787a 100644
--- a/tests/cli/test_argparse.py
+++ b/tests/cli/test_argparse.py
@@ -12,7 +12,6 @@ def test_positive_number():
 
 
 def test_handle_kv_string():
-
     assert {"tags": "fail"} == handle_kv_string("tags=fail")
 
     assert {"tags": "fail", "type": "script"} == handle_kv_string(
@@ -25,7 +24,6 @@ def test_handle_kv_string():
 
 
 def test_arg_parse():
-
     parser = get_parser()
     print(parser)
 
diff --git a/tests/cli/test_build.py b/tests/cli/test_build.py
index 535e2a2c0..f7778cfc7 100644
--- a/tests/cli/test_build.py
+++ b/tests/cli/test_build.py
@@ -29,7 +29,6 @@ def test_clean():
 
 @pytest.mark.cli
 def test_build_by_tags():
-
     system = BuildTestSystem()
 
     # ensure we rebuild cache file before running any buildspecs commands
@@ -50,7 +49,6 @@ def test_build_by_tags():
 
 @pytest.mark.cli
 def test_build_rerun():
-
     system = BuildTestSystem()
 
     #  testing buildtest build --rerun
@@ -64,7 +62,6 @@ def test_build_rerun():
 
 @pytest.mark.cli
 def test_build_executor_type():
-
     system = BuildTestSystem()
     #  testing buildtest build --tags python --executor-type local
     cmd = BuildTest(
@@ -88,7 +85,6 @@ def test_build_executor_type():
 
 @pytest.mark.cli
 def test_build_filter_check():
-
     system = BuildTestSystem()
 
     #  testing buildtest build --tags pass --filter tags=pass
@@ -149,7 +145,6 @@ def test_build_filter_check():
 
 @pytest.mark.cli
 def test_build_buildspecs():
-
     system = BuildTestSystem()
 
     buildspec_paths = os.path.join(test_root, "buildsystem", "valid_buildspecs")
@@ -185,9 +180,23 @@ def test_build_buildspecs():
         cmd.build()
 
 
+def test_run_all_perf_checks():
+    system = BuildTestSystem()
+    buildspecs = walk_tree(
+        root_dir=os.path.join(BUILDTEST_ROOT, "tutorials", "perf_checks"), ext=".yml"
+    )
+
+    #  buildtest build --buildspec $BUILDTEST_ROOT/tutorials/perf_checks
+    cmd = BuildTest(
+        configuration=configuration,
+        buildspecs=buildspecs,
+        buildtest_system=system,
+    )
+    cmd.build()
+
+
 @pytest.mark.cli
 def test_buildspec_tag_executor():
-
     system = BuildTestSystem()
 
     # testing buildtest build --tags fail --executor generic.local.csh
@@ -202,7 +211,6 @@ def test_buildspec_tag_executor():
 
 @pytest.mark.cli
 def test_build_csh_executor():
-
     if not shutil.which("csh"):
         pytest.skip("Unable to run this test since it requires 'csh'")
 
@@ -219,7 +227,6 @@ def test_build_csh_executor():
 
 @pytest.mark.cli
 def test_skip_field():
-
     system = BuildTestSystem()
 
     cmd = BuildTest(
@@ -240,7 +247,6 @@ def test_skip_field():
 
 @pytest.mark.cli
 def test_build_by_stages():
-
     system = BuildTestSystem()
 
     # testing buildtest build --tags python --stage=parse
@@ -264,7 +270,6 @@ def test_build_by_stages():
 
 @pytest.mark.cli
 def test_build_rebuild():
-
     system = BuildTestSystem()
 
     buildspec_file = os.path.join(BUILDTEST_ROOT, "tutorials", "python-shell.yml")
@@ -280,8 +285,52 @@ def test_build_rebuild():
 
 
 @pytest.mark.cli
-def test_invalid_buildspes():
+def test_build_limit():
+    system = BuildTestSystem()
 
+    # rebuild 5 times (buildtest build -t python --rebuild=5 --limit=2
+    cmd = BuildTest(
+        configuration=configuration,
+        tags=["python"],
+        rebuild=5,
+        buildtest_system=system,
+        limit=2,
+    )
+    cmd.build()
+
+    # should raise error when --limit=0 testing special case
+    with pytest.raises(BuildTestError):
+        BuildTest(
+            configuration=configuration,
+            tags=["python"],
+            rebuild=5,
+            buildtest_system=system,
+            limit=0,
+        )
+
+    # should raise error when --limit is a negative number
+    with pytest.raises(BuildTestError):
+        BuildTest(
+            configuration=configuration,
+            tags=["python"],
+            rebuild=5,
+            buildtest_system=system,
+            limit=-99,
+        )
+
+    # should raise error when --limit is not an int
+    with pytest.raises(BuildTestError):
+        BuildTest(
+            configuration=configuration,
+            tags=["python"],
+            rebuild=5,
+            buildtest_system=system,
+            limit=1.5,
+        )
+
+
+@pytest.mark.cli
+def test_invalid_buildspes():
     system = BuildTestSystem()
 
     buildspec_file = [
@@ -312,8 +361,8 @@ def test_jobdeps():
     cmd.build()
 
 
+@pytest.mark.cli
 def test_timeout():
-
     system = BuildTestSystem()
 
     buildspecs = [os.path.join(BUILDTEST_ROOT, "tutorials", "sleep.yml")]
@@ -336,8 +385,7 @@ def test_timeout():
 
 
 @pytest.mark.cli
-def test_remove_stagedi():
-
+def test_remove_stagedir():
     system = BuildTestSystem()
 
     buildspec_file = [os.path.join(BUILDTEST_ROOT, "tutorials", "python-shell.yml")]
@@ -351,7 +399,6 @@ def test_remove_stagedi():
 
 
 def test_discover():
-
     # test single buildspec file
     buildspec = [os.path.join(valid_buildspecs, "environment.yml")]
 
@@ -380,7 +427,6 @@ def test_discover():
 
 
 def test_BuildTest_type():
-
     # buildspec must be a list not a string
     buildspec = os.path.join(valid_buildspecs, "environment.yml")
     with pytest.raises(BuildTestError):
diff --git a/tests/cli/test_buildspec.py b/tests/cli/test_buildspec.py
index e140c5fd2..d59c9dd15 100644
--- a/tests/cli/test_buildspec.py
+++ b/tests/cli/test_buildspec.py
@@ -27,7 +27,6 @@
 
 @pytest.mark.cli
 def test_buildspec_validate():
-
     buildspecs = [
         os.path.join(BUILDTEST_ROOT, "tutorials", "vars.yml"),
         os.path.join(BUILDTEST_ROOT, "tutorials", "compilers"),
@@ -54,7 +53,6 @@ def test_buildspec_validate():
 
 @pytest.mark.cli
 def test_func_buildspec_find():
-
     # buildtest buildspec find --rebuild --terse --no-header
     cache = BuildspecCache(
         rebuild=True, configuration=configuration, terse=True, header=False
@@ -97,16 +95,16 @@ def test_func_buildspec_find():
     # buildtest buildspec find --group-by-tags
     cache.print_by_tags()
 
-    # implements buildtest buildspec find --helpfilter
+    # buildtest buildspec find --helpfilter
     cache.print_filter_fields()
 
-    # implements buildtest buildspec find --helpformat
+    # buildtest buildspec find --helpformat
     cache.print_format_fields()
 
-    # implements buildtest buildspec find --filterfields
+    # buildtest buildspec find --filterfields
     cache.print_raw_filter_fields()
 
-    # implements buildtest buildspec find --formatfields
+    # buildtest buildspec find --formatfields
     cache.print_raw_format_fields()
 
     # buildtest buildspec find --pager
@@ -123,23 +121,46 @@ def test_func_buildspec_find():
     # buildtest buildspec find --quiet
     cache.print_buildspecs(quiet=True)
 
-    # test buildspec cache with color 'blue'
+    # buildtest buildspec find --row-count
+    cache = BuildspecCache(configuration=configuration)
+    cache.print_buildspecs(row_count=True)
+
+    # buildtest buildspec find --row-count --buildspec
+    cache.print_buildspecfiles(row_count=True)
+
+    # buildtest buildspec find --row-count --tags
+    cache.print_tags(row_count=True)
+
+    # buildtest buildspec find --row-count --executors
+    cache.print_executors(row_count=True)
+
+    # test all commands with color 'blue'
+    # buildtest --color blue buildspec find --rebuild
     cache = BuildspecCache(rebuild=True, configuration=configuration, color="blue")
+    # buildtest --color blue buildspec find
     cache.print_buildspecs()
+    # buildtest --color blue buildspec find -b
     cache.print_buildspecfiles()
+    # buildtest --color blue buildspec find -e
     cache.print_executors()
+    # buildtest --color blue buildspec find -t
     cache.print_tags()
+    # buildtest --color blue buildspec find --group-by-executor
     cache.print_by_executors()
+    # buildtest --color blue buildspec find --group-by-tags
     cache.print_by_tags()
+    # buildtest --color blue buildspec find --helpfilter
     cache.print_filter_fields()
+    # buildtest --color blue buildspec find --helpformat
     cache.print_format_fields()
+    # buildtest --color blue buildspec find --filterfields
     cache.print_raw_filter_fields()
+    # buildtest --color blue buildspec find --formatfields
     cache.print_raw_format_fields()
 
 
 @pytest.mark.cli
 def test_buildspec_find_terse():
-
     cache = BuildspecCache(
         configuration=configuration,
         terse=True,
@@ -229,12 +250,11 @@ def test_edit_file():
 
 @pytest.mark.cli
 def test_buildspec_find_filter():
-
-    # testing buildtest buildspec find --filter tags=fail
+    # buildtest buildspec find --filter tags=fail
     cache = BuildspecCache(filterfields={"tags": "fail"}, configuration=configuration)
     cache.print_buildspecs()
 
-    # testing buildtest buildspec find --filter buildspec=$BUILDTEST_ROOT/tutorials/hello_world.yml
+    # buildtest buildspec find --filter buildspec=$BUILDTEST_ROOT/tutorials/hello_world.yml
     cache = BuildspecCache(
         filterfields={
             "buildspec": os.path.join(BUILDTEST_ROOT, "tutorials", "hello_world.yml")
@@ -243,7 +263,7 @@ def test_buildspec_find_filter():
     )
     cache.print_buildspecs()
 
-    # testing buildtest buildspec find --filter type=script,executor=generic.local.sh,tags=fail
+    # buildtest buildspec find --filter type=script,executor=generic.local.sh,tags=fail
     cache = BuildspecCache(
         filterfields={
             "type": "script",
@@ -271,7 +291,7 @@ def test_buildspec_find_filter():
         # if we specify a directory path for buildspec filter this will raise an exception.
         BuildspecCache(filterfields={"buildspec": tf.name}, configuration=configuration)
 
-    # testing buildtest buildspec find --filter key1=val1,key2=val2
+    # buildtest buildspec find --filter key1=val1,key2=val2
     with pytest.raises(BuildTestError):
         cache = BuildspecCache(
             filterfields={"key1": "val1", "key2": "val2"}, configuration=configuration
@@ -281,8 +301,7 @@ def test_buildspec_find_filter():
 
 @pytest.mark.cli
 def test_buildspec_find_format():
-
-    # testing buildtest buildspec find --format name,type,tags,executor,description,buildspec
+    # buildtest buildspec find --format name,type,tags,executor,description,buildspec
     cache = BuildspecCache(
         formatfields="name,type,tags,executor,description,buildspec",
         configuration=configuration,
@@ -291,23 +310,21 @@ def test_buildspec_find_format():
 
     # Any invalid format fields will raise an exception of type BuildTestError
     with pytest.raises(BuildTestError):
-
-        # testing buildtest buildspec find --format field1
+        # buildtest buildspec find --format field1
         cache = BuildspecCache(formatfields="field1", configuration=configuration)
         cache.print_buildspecs()
 
 
 @pytest.mark.cli
 def test_buildspec_find_roots():
-
     root_buildspecs = [
         os.path.join(BUILDTEST_ROOT, "tests", "buildsystem"),
         os.path.join(BUILDTEST_ROOT, "tutorials"),
     ]
-    # testing buildtest buildspec find --root $BUILDTEST_ROOT/tests/buildsystem --root $BUILDTEST_ROOT/tutorials
+    # buildtest buildspec find --root $BUILDTEST_ROOT/tests/buildsystem --root $BUILDTEST_ROOT/tutorials
     BuildspecCache(roots=root_buildspecs, configuration=configuration)
 
-    # running buildtest buildspec find --root $BUILDTEST_ROOT/README.rst --root $BUILDTEST_ROOT/environment.yml
+    # buildtest buildspec find --root $BUILDTEST_ROOT/README.rst --root $BUILDTEST_ROOT/environment.yml
     BuildspecCache(
         roots=[
             os.path.join(BUILDTEST_ROOT, "README.rst"),
@@ -319,10 +336,11 @@ def test_buildspec_find_roots():
 
 @pytest.mark.cli
 def test_buildspec_summary():
-    # test buildtest buildspec summary
+    # buildtest buildspec summary
     summarize_buildspec_cache(
         configuration=configuration, pager=False, color=Color.default().name
     )
+    # buildtest buildspec summary --pager
     summarize_buildspec_cache(configuration=configuration, pager=True)
 
 
@@ -332,10 +350,10 @@ def test_buildspec_show():
 
     test_name = cache.get_random_tests(num_items=1)
 
-    # run buildtest buildspec show <test>
+    # buildtest buildspec show <test>
     show_buildspecs(test_name, configuration)
 
-    # run buildtest buildspec <test> show --theme monokai
+    # buildtest buildspec <test> show --theme monokai
     show_buildspecs(test_name, configuration, theme="monokai")
 
     # testing invalid buildspec name, it should not raise exception
@@ -345,7 +363,6 @@ def test_buildspec_show():
 
 @pytest.mark.cli
 def test_buildspec_show_fail():
-
     # Query some random test name that doesn't exist
 
     random_testname = "".join(random.choices(string.ascii_letters, k=10))
@@ -360,7 +377,7 @@ def test_buildspec_show_fail():
     report = Report()
     # get a random failed test from report file to be used for showing content of buildspec file
     fail_tests = random.sample(report.get_test_by_state(state="FAIL"), 1)
-    # running buildtest buildspec show-fail <test> --theme monokai
+    # buildtest buildspec show-fail <test> --theme monokai
     show_failed_buildspecs(
         configuration=configuration, test_names=fail_tests, theme="monokai"
     )
diff --git a/tests/cli/test_cdash.py b/tests/cli/test_cdash.py
index 1623a2d4c..9df29148b 100644
--- a/tests/cli/test_cdash.py
+++ b/tests/cli/test_cdash.py
@@ -16,7 +16,6 @@
 
 @pytest.mark.cli
 def test_cdash_view():
-
     cdash_config = deep_get(configuration.target_config, "cdash")
     view_cdash_project(
         cdash_config=cdash_config, config_file=configuration.file, open_browser=False
@@ -25,7 +24,6 @@ def test_cdash_view():
 
 @pytest.mark.cli
 def test_cdash_upload():
-
     system = BuildTestSystem()
     cmd = BuildTest(
         buildspecs=[os.path.join(BUILDTEST_ROOT, "tutorials", "shell_examples.yml")],
@@ -43,7 +41,6 @@ def test_cdash_upload():
 
 
 def test_cdash_upload_exceptions():
-
     # a buildname must be specified, a None will result in error
     with pytest.raises(SystemExit):
         upload_test_cdash(
diff --git a/tests/cli/test_compilers/invalid_moduletool.yml b/tests/cli/test_compilers/invalid_moduletool.yml
index 208afcd8b..afd4d6c73 100644
--- a/tests/cli/test_compilers/invalid_moduletool.yml
+++ b/tests/cli/test_compilers/invalid_moduletool.yml
@@ -25,4 +25,4 @@ system:
           builtin_gcc:
             cc: /usr/bin/gcc
             fc: /usr/bin/gfortran
-            cxx: /usr/bin/g++
\ No newline at end of file
+            cxx: /usr/bin/g++
diff --git a/tests/cli/test_compilers/missing_compiler_find.yml b/tests/cli/test_compilers/missing_compiler_find.yml
index a0f9300c7..227996d41 100644
--- a/tests/cli/test_compilers/missing_compiler_find.yml
+++ b/tests/cli/test_compilers/missing_compiler_find.yml
@@ -25,4 +25,4 @@ system:
           builtin_gcc:
             cc: /usr/bin/gcc
             fc: /usr/bin/gfortran
-            cxx: /usr/bin/g++
\ No newline at end of file
+            cxx: /usr/bin/g++
diff --git a/tests/cli/test_config.py b/tests/cli/test_config.py
index fc15874fb..8ca1016a4 100644
--- a/tests/cli/test_config.py
+++ b/tests/cli/test_config.py
@@ -27,7 +27,6 @@
 
 @pytest.mark.cli
 def test_config_systems():
-
     schema_files = os.path.join(
         SCHEMA_ROOT, "examples", "settings.schema.json", "valid"
     )
@@ -41,15 +40,14 @@ def test_config_systems():
 @pytest.mark.cli
 def test_view_configuration():
     view_configuration(configuration)
-    # testing -  buildtest config view --theme emacs
+    # buildtest config view --theme emacs
     view_configuration(configuration, theme="emacs")
 
-    # testing - buildtest config view --pager
+    # buildtest config view --pager
     view_configuration(configuration, pager=True)
 
 
 def test_valid_config_schemas():
-
     valid_schema_dir = os.path.join(pytest_root, "examples", "config_schemas", "valid")
     schema_config = load_schema(DEFAULT_SETTINGS_SCHEMA)
     for schema in walk_tree(valid_schema_dir, ".yml"):
@@ -71,7 +69,7 @@ def test_config_path():
 def test_config_executors():
     buildexecutor = BuildExecutor(configuration)
 
-    # run buildtest config executors --json
+    # buildtest config executors --json
     view_executors(
         configuration=configuration,
         buildexecutor=buildexecutor,
@@ -81,7 +79,7 @@ def test_config_executors():
         invalid=False,
     )
 
-    # run buildtest config executors --yaml
+    # buildtest config executors --yaml
     view_executors(
         configuration=configuration,
         buildexecutor=buildexecutor,
@@ -91,7 +89,7 @@ def test_config_executors():
         invalid=False,
     )
 
-    # run buildtest config executors -d
+    # buildtest config executors -d
     view_executors(
         configuration=configuration,
         buildexecutor=buildexecutor,
@@ -101,7 +99,7 @@ def test_config_executors():
         invalid=False,
     )
 
-    # run buildtest config executors -i
+    # buildtest config executors -i
     view_executors(
         configuration=configuration,
         buildexecutor=buildexecutor,
@@ -111,7 +109,7 @@ def test_config_executors():
         invalid=True,
     )
 
-    # run buildtest config executors
+    # buildtest config executors
     view_executors(
         configuration=configuration,
         buildexecutor=buildexecutor,
@@ -132,7 +130,7 @@ def test_disabled_invalid_executors():
 
     print("reading config file:", configfile)
     be = BuildExecutor(configuration)
-    # run buildtest config executors
+    # buildtest config executors -d
     view_executors(
         configuration=configuration,
         buildexecutor=be,
@@ -142,7 +140,7 @@ def test_disabled_invalid_executors():
         invalid=False,
     )
 
-    # run buildtest config executors
+    # buildtest config executors -i
     view_executors(
         configuration=configuration,
         buildexecutor=be,
diff --git a/tests/cli/test_help.py b/tests/cli/test_help.py
index f74dfe3bd..3b3babdcf 100644
--- a/tests/cli/test_help.py
+++ b/tests/cli/test_help.py
@@ -6,6 +6,7 @@ def test_buildtest_help():
     buildtest_help(command="buildspec")
     buildtest_help(command="config")
     buildtest_help(command="cdash")
+    buildtest_help(command="path")
     buildtest_help(command="history")
     buildtest_help(command="inspect")
     buildtest_help(command="report")
diff --git a/tests/cli/test_history.py b/tests/cli/test_history.py
index 395d7029e..f2bcb59cb 100644
--- a/tests/cli/test_history.py
+++ b/tests/cli/test_history.py
@@ -32,7 +32,6 @@ def test_build_history_list():
 
 
 def test_build_history_query():
-
     fname = os.path.join(VAR_DIR, "output.txt")
     configuration = SiteConfiguration()
 
@@ -66,7 +65,6 @@ def test_build_history_query():
 
 
 def test_invalid_buildid():
-
     with pytest.raises(SystemExit):
         query_builds(build_id=-1, log_option=True, output=False)
 
diff --git a/tests/cli/test_info.py b/tests/cli/test_info.py
index 2db7449a3..64c76812c 100644
--- a/tests/cli/test_info.py
+++ b/tests/cli/test_info.py
@@ -4,7 +4,6 @@
 
 
 def test_buildtest_info():
-
     config = SiteConfiguration()
     config.detect_system()
     config.validate()
diff --git a/tests/cli/test_inspect.py b/tests/cli/test_inspect.py
index de3412d98..64f8a872c 100644
--- a/tests/cli/test_inspect.py
+++ b/tests/cli/test_inspect.py
@@ -2,7 +2,6 @@
 import random
 import string
 import tempfile
-import uuid
 
 import pytest
 from buildtest.cli.inspect import inspect_cmd
@@ -12,7 +11,6 @@
 
 
 def test_buildtest_inspect_list():
-
     # running buildtest inspect list
     class args:
         subcommands = "inspect"
@@ -59,7 +57,6 @@ class args:
 
 
 def test_buildtest_inspect_name():
-
     r = Report()
 
     # select a random test name
@@ -75,21 +72,9 @@ class args:
     # buildtest inspect name <name1> <name2>
     inspect_cmd(args)
 
-    test_names = r.get_random_builder_names(num_items=2)
-
-    class args:
-        subcommands = "inspect"
-        inspect = "name"
-        name = test_names
-        report = None
-
-    print(f"Querying test names: {args.name}")
-    # buildtest inspect name <name1>/<ID> <name2>/<ID>
-    inspect_cmd(args)
-
     random_test = [
         "".join(random.choices(string.ascii_letters, k=10)),
-        "".join(random.choices(string.ascii_letters, k=10)) + "/" + str(uuid.uuid4()),
+        "".join(random.choices(string.ascii_letters, k=10)),
     ]
 
     class args:
@@ -112,7 +97,6 @@ class args:
 
 
 def test_buildspec_inspect_buildspec():
-
     tf = tempfile.NamedTemporaryFile(delete=True)
 
     class args:
@@ -159,7 +143,6 @@ class args:
 
 
 def test_buildtest_query():
-
     report = Report()
     names = report.get_names()
 
@@ -172,10 +155,26 @@ class args:
         testpath = True
         buildscript = True
         buildenv = True
+        theme = "emacs"
 
     # check buildtest inspect query --output --error --testpath --buildscript --buildenv <name1> <name2> ...
     inspect_cmd(args)
 
+    class args:
+        subcommands = "inspect"
+        inspect = "query"
+        name = ["stream_test"]
+        output = False
+        error = False
+        testpath = False
+        buildscript = False
+        buildenv = False
+        theme = None
+
+    # buildtest inspect query stream_test
+    # the 'stream_test' will add coverage where metrics are printed in output of 'buildtest inspect query'
+    inspect_cmd(args)
+
     class args:
         subcommands = "inspect"
         inspect = "query"
@@ -186,6 +185,7 @@ class args:
         testpath = False
         buildscript = False
         buildenv = False
+        theme = None
 
     # check invalid test name when querying result which will result in exception SystemExit
     with pytest.raises(SystemExit):
diff --git a/tests/cli/test_path.py b/tests/cli/test_path.py
index 376efd2ce..559ceb3b4 100644
--- a/tests/cli/test_path.py
+++ b/tests/cli/test_path.py
@@ -7,7 +7,6 @@
 
 
 def test_path():
-
     report = Report()
     names = report.get_names()
     name = names[0]
diff --git a/tests/cli/test_report.py b/tests/cli/test_report.py
index 96edc1a42..f811b1f80 100644
--- a/tests/cli/test_report.py
+++ b/tests/cli/test_report.py
@@ -13,7 +13,6 @@
 
 @pytest.mark.cli
 def test_report():
-
     # assert os.path.exists(BUILD_REPORT)
 
     result = Report()
@@ -44,7 +43,6 @@ def test_report():
 
 @pytest.mark.cli
 def test_report_format():
-
     # buildtest report --helpformat
     report = Report()
     report.print_format_fields()
@@ -59,7 +57,6 @@ def test_report_format():
 
 @pytest.mark.cli
 def test_report_filter():
-
     # run 'buildtest report --helpfilter'
     report = Report()
     report.print_filter_fields()
@@ -113,7 +110,6 @@ def test_report_filter():
 
 @pytest.mark.cli
 def test_report_oldest_and_latest():
-
     # buildtest report --filter tags=tutorials --latest
     Report(filter_args={"tags": "tutorials"}, latest=True)
 
@@ -126,21 +122,18 @@ def test_report_oldest_and_latest():
 
 @pytest.mark.cli
 def test_report_failure():
-
     # buildtest report --filter tags=tutorials --failure
     Report(filter_args={"tags": "tutorials"}, failure=True)
 
 
 @pytest.mark.cli
 def test_report_passed():
-
     # buildtest report --filter tags=tutorials --passed
     Report(filter_args={"tags": "tutorials"}, passed=True)
 
 
 @pytest.mark.cli
 def test_report_start_and_end():
-
     start_date = datetime.datetime.strptime("2022-06-07 00:00:00", "%Y-%m-%d %X")
     end_date = datetime.datetime.now()
 
@@ -156,7 +149,6 @@ def test_report_start_and_end():
 
 @pytest.mark.cli
 def test_invalid_filters():
-
     # run 'buildtest report --filter state=UNKNOWN --format name,state',
     # this raises error because UNKNOWN is not valid value for state field
     with pytest.raises(BuildTestError):
@@ -187,7 +179,6 @@ def test_invalid_filters():
 
 @pytest.mark.cli
 def test_invalid_report_files():
-
     tf = tempfile.NamedTemporaryFile(delete=True)
 
     # reading a report file not in JSON format will result in exception BuildTestError
@@ -210,12 +201,15 @@ def test_report_summary():
     report = Report()
     report_summary(report)
 
+    # buildtest rt summary --pager
     report = Report(pager=True)
     report_summary(report)
 
+    # buildtest rt summary --detailed
     report = Report()
     report_summary(report, detailed=True)
 
+    # buildtest rt summary --detailed --pager
     report = Report(pager=True)
     report_summary(report, detailed=True)
 
@@ -228,24 +222,18 @@ def test_report_summary():
     )
     report_summary(report, detailed=True)
 
+    # buildtest --color light_pink1 rt sm --detailed
     report = Report(color="light_pink1")
     report_summary(report, detailed=True)
 
-    report = Report(pager=True, color="light_pink1")
-    report_summary(report)
-
-    report = Report(color="BAD_COLOR")
-    report_summary(report)
-
-    report = Report(pager=True, color="BAD_COLOR")
+    # buildtest rt sm --detailed --pager
+    report = Report(pager=True)
     report_summary(report, detailed=True)
 
+    # buildtest --color BAD_COLOR rt sm --detailed
     report = Report(color="BAD_COLOR")
     report_summary(report, detailed=True)
 
-    report = Report(pager=True, color="BAD_COLOR")
-    report_summary(report)
-
 
 @pytest.mark.cli
 def test_report_list():
@@ -314,7 +302,6 @@ class args:
 
 @pytest.mark.cli
 def test_report_limited_rows():
-
     report = Report()
     report.print_report(count=5)
     report.print_report(terse=True, count=5)
diff --git a/tests/cli/test_schema.py b/tests/cli/test_schema.py
index 31c5be115..580ac32c1 100644
--- a/tests/cli/test_schema.py
+++ b/tests/cli/test_schema.py
@@ -5,7 +5,6 @@
 @pytest.mark.schema
 @pytest.mark.cli
 def test_schema_cmd():
-
     supported_schemas = [
         "definitions.schema.json",
         "global.schema.json",
diff --git a/tests/examples/ascent/lsf_job_state.yml b/tests/examples/ascent/lsf_job_state.yml
index 0a67b4111..d14692614 100644
--- a/tests/examples/ascent/lsf_job_state.yml
+++ b/tests/examples/ascent/lsf_job_state.yml
@@ -7,4 +7,4 @@ buildspecs:
     bsub: ["-W 10", "-nnodes 1"]
     run: jsrun hostname
     status:
-      lsf_job_state: EXIT
\ No newline at end of file
+      lsf_job_state: EXIT
diff --git a/tests/examples/cori/exists.yml b/tests/examples/cori/exists.yml
index 8d498cfd3..f8c8f3ef9 100644
--- a/tests/examples/cori/exists.yml
+++ b/tests/examples/cori/exists.yml
@@ -1,17 +1,17 @@
 buildspecs:
   status_exists_slurm:
-   type: script
-   executor: cori.slurm.knl_debug
-   description: status check based for file and directory
-   sbatch: ['-n 1', '-t 5', '-C knl']
-   run: |
-     mkdir -p a/b/c
-     touch foo
-   status:
-     exists:
-       - a/b/c
-       - foo
-     is_dir:
-       - a/b/c
-     is_file:
-       - foo
\ No newline at end of file
+    type: script
+    executor: cori.slurm.knl_debug
+    description: status check based for file and directory
+    sbatch: ['-n 1', '-t 5', '-C knl']
+    run: |
+      mkdir -p a/b/c
+      touch foo
+    status:
+      exists:
+        - a/b/c
+        - foo
+      is_dir:
+        - a/b/c
+      is_file:
+        - foo
diff --git a/tests/examples/cori/hostname.yml b/tests/examples/cori/hostname.yml
index c8cadbcbb..13adaf897 100644
--- a/tests/examples/cori/hostname.yml
+++ b/tests/examples/cori/hostname.yml
@@ -5,4 +5,3 @@ buildspecs:
     description: "Run hostname via KNL debug queue"
     sbatch: ["-t 5", "-n 1"]
     run: hostname
-
diff --git a/tests/examples/jlse/hold_job.yml b/tests/examples/jlse/hold_job.yml
index 30f2577b3..311a07df3 100644
--- a/tests/examples/jlse/hold_job.yml
+++ b/tests/examples/jlse/hold_job.yml
@@ -5,4 +5,4 @@ buildspecs:
     tags: [jobs]
     description: Hold Job on testing queue
     cobalt: ["-n 1", "-t 10", "-h"]
-    run: hostname
\ No newline at end of file
+    run: hostname
diff --git a/tests/examples/jlse/hostname.yml b/tests/examples/jlse/hostname.yml
index 612649b04..72207373b 100644
--- a/tests/examples/jlse/hostname.yml
+++ b/tests/examples/jlse/hostname.yml
@@ -5,4 +5,4 @@ buildspecs:
     tags: [jobs]
     description: Run hostname on testing queue
     cobalt: ["-n 1", "-t 10"]
-    run: hostname
\ No newline at end of file
+    run: hostname
diff --git a/tests/examples/pbs/hold.yml b/tests/examples/pbs/hold.yml
index 072bc8227..6928374e2 100644
--- a/tests/examples/pbs/hold.yml
+++ b/tests/examples/pbs/hold.yml
@@ -4,4 +4,4 @@ buildspecs:
     executor: generic.pbs.workq
     description: PBS Hold Job
     pbs: ["-l nodes=1", "-l walltime=00:02:00", "-h"]
-    run: hostname
\ No newline at end of file
+    run: hostname
diff --git a/tests/examples/pbs/job_dependency.yml b/tests/examples/pbs/job_dependency.yml
index 3c9b5dd70..0c3d29096 100644
--- a/tests/examples/pbs/job_dependency.yml
+++ b/tests/examples/pbs/job_dependency.yml
@@ -11,4 +11,4 @@ buildspecs:
     executor: generic.pbs.workq
     needs: [hostname]
     pbs: ["-l nodes=1", "-l walltime=00:02:00"]
-    run: whoami
\ No newline at end of file
+    run: whoami
diff --git a/tests/examples/pbs/multiple_tests.yml b/tests/examples/pbs/multiple_tests.yml
index 369f8fe5a..66dd24ba8 100644
--- a/tests/examples/pbs/multiple_tests.yml
+++ b/tests/examples/pbs/multiple_tests.yml
@@ -19,4 +19,4 @@ buildspecs:
     type: script
     executor: generic.pbs.workq
     pbs: ["-l nodes=1", "-l walltime=00:01:00"]
-    run: echo "This is a sample job 3"
\ No newline at end of file
+    run: echo "This is a sample job 3"
diff --git a/tests/executors/test_base.py b/tests/executors/test_base.py
index 4e7c9acab..62d111fcf 100644
--- a/tests/executors/test_base.py
+++ b/tests/executors/test_base.py
@@ -17,7 +17,6 @@
 
 
 def test_build_executor(tmp_path):
-
     bc = SiteConfiguration()
     bc.detect_system()
     bc.validate(validate_executors=True)
diff --git a/tests/schema_tests/test_compiler.py b/tests/schema_tests/test_compiler.py
index 1fac10ce0..f484455f7 100644
--- a/tests/schema_tests/test_compiler.py
+++ b/tests/schema_tests/test_compiler.py
@@ -47,7 +47,6 @@ def check_valid_recipes(recipes, valids, loaded):
 
 @pytest.mark.schema
 def test_compiler_examples():
-
     print("Testing schema %s" % schema_file)
     loaded = load_schema(schema_path)
     assert isinstance(loaded, dict)
diff --git a/tests/schema_tests/test_settings.py b/tests/schema_tests/test_settings.py
index 7c752bebc..b374b9efc 100644
--- a/tests/schema_tests/test_settings.py
+++ b/tests/schema_tests/test_settings.py
@@ -12,7 +12,6 @@
 
 @pytest.mark.schema
 def test_settings_examples():
-
     # load schema and ensure type is a dict
     recipe = load_schema(settings_schema)
 
@@ -23,7 +22,6 @@ def test_settings_examples():
     assert valid_recipes
     # check all valid recipes
     for example in valid_recipes:
-
         filepath = os.path.join(valid, example)
         print(f"Loading Recipe File: {filepath}")
         example_recipe = load_recipe(filepath)
diff --git a/tests/schema_tests/test_spack.py b/tests/schema_tests/test_spack.py
index 84c8184d3..f09a0ca71 100644
--- a/tests/schema_tests/test_spack.py
+++ b/tests/schema_tests/test_spack.py
@@ -47,7 +47,6 @@ def check_valid_recipes(recipes, valids, loaded):
 
 @pytest.mark.schema
 def test_spack_examples():
-
     print("Testing schema %s" % schema_file)
     loaded = load_schema(schema_path)
 
diff --git a/tests/schemas/test_utils.py b/tests/schemas/test_utils.py
index 90f9c63b2..b44626ba7 100644
--- a/tests/schemas/test_utils.py
+++ b/tests/schemas/test_utils.py
@@ -12,7 +12,6 @@
     reason="Invalid File Extension for loading schema", raises=SystemExit
 )
 def test_load_schema_invalid_ext():
-
     # invalid file extension should fail
     load_schema(os.path.join(root, "README.rst"))
 
@@ -32,7 +31,6 @@ def test_load_schema_invalid_path():
 @pytest.mark.utility
 @pytest.mark.xfail(reason="Invalid File Path when loading recipe", raises=SystemExit)
 def test_load_recipe_invalid_path():
-
     fp = tempfile.NamedTemporaryFile()
     assert os.path.exists(fp.name)
     fp.close()
diff --git a/tests/settings/nersc.yml b/tests/settings/nersc.yml
index 3f584d548..39b6504d6 100644
--- a/tests/settings/nersc.yml
+++ b/tests/settings/nersc.yml
@@ -92,4 +92,4 @@ system:
           shell: csh
         zsh:
           description: submit jobs on local machine using zsh shell
-          shell: zsh
\ No newline at end of file
+          shell: zsh
diff --git a/tests/settings/pbs.yml b/tests/settings/pbs.yml
index 12803733c..dd245194e 100644
--- a/tests/settings/pbs.yml
+++ b/tests/settings/pbs.yml
@@ -4,8 +4,8 @@ system:
     moduletool: N/A
     executors:
       defaults:
-         pollinterval: 5
-         maxpendtime: 30
+        pollinterval: 5
+        maxpendtime: 30
       local:
         bash:
           description: submit jobs on local machine using bash shell
diff --git a/tests/sitecustomize.py b/tests/sitecustomize.py
new file mode 100644
index 000000000..ae545c286
--- /dev/null
+++ b/tests/sitecustomize.py
@@ -0,0 +1,3 @@
+import coverage
+
+coverage.process_startup()
diff --git a/tests/test_ascent.py b/tests/test_ascent.py
index 263fba2a9..8be56bada 100644
--- a/tests/test_ascent.py
+++ b/tests/test_ascent.py
@@ -50,7 +50,6 @@ def test_ascent():
 
 
 def test_compilers_find_ascent():
-
     if not hostname.endswith("ascent.olcf.ornl.gov"):
         pytest.skip("This test must run on domain ascent.olcf.ornl.gov")
 
diff --git a/tests/test_jlse.py b/tests/test_jlse.py
index f9a25e204..b7ee81cdf 100644
--- a/tests/test_jlse.py
+++ b/tests/test_jlse.py
@@ -12,7 +12,6 @@
 
 
 def test_jlse():
-
     if not hostname.endswith("alcf.anl.gov"):
         pytest.skip("Test runs only on JLSE Login Nodes with domain name alcf.anl.gov")
 
diff --git a/tests/test_log.py b/tests/test_log.py
index a4b56c75a..d02789fd5 100644
--- a/tests/test_log.py
+++ b/tests/test_log.py
@@ -5,7 +5,6 @@
 
 
 def test_BuildTestLogger():
-
     tf = tempfile.NamedTemporaryFile()
 
     logger = init_logfile(tf.name)
diff --git a/tests/test_nersc.py b/tests/test_nersc.py
index dd58db1e9..de3b3801e 100644
--- a/tests/test_nersc.py
+++ b/tests/test_nersc.py
@@ -45,7 +45,6 @@ def test_cori_burstbuffer():
 
 
 def test_cori_slurm_hostname():
-
     if not hostname.startswith("cori"):
         pytest.skip("This test runs on Cori Login nodes ('cori*')")
 
@@ -69,7 +68,6 @@ def test_cori_slurm_hostname():
 
 
 def test_cori_slurm_max_pend():
-
     if not hostname.startswith("cori"):
         pytest.skip("This test runs on Cori Login nodes ('cori*')")
 
@@ -95,7 +93,6 @@ def test_cori_slurm_max_pend():
 
 
 def test_cori_slurm_file_exists():
-
     if not hostname.startswith("cori"):
         pytest.skip("This test runs on Cori Login nodes ('cori*')")
 
@@ -120,7 +117,6 @@ def test_cori_slurm_file_exists():
 
 
 def test_compiler_find_cori():
-
     if not hostname.startswith("cori"):
         pytest.skip("This test runs on Cori Login nodes ('cori*')")
 
@@ -137,7 +133,6 @@ def test_compiler_find_cori():
 
 
 def test_compiler_test_cori():
-
     if not hostname.startswith("cori"):
         pytest.skip("This test runs on Cori Login nodes ('cori*')")
 
@@ -150,7 +145,6 @@ def test_compiler_test_cori():
 
 
 def test_compiler_find_perlmutter():
-
     if not hostname.startswith("login"):
         pytest.skip("This test runs on Perlmutter Login nodes ('login*')")
 
@@ -167,7 +161,6 @@ def test_compiler_find_perlmutter():
 
 
 def test_compiler_test_perlmutter():
-
     if not hostname.startswith("login"):
         pytest.skip("This test runs on Perlmutter Login nodes ('login*')")
 
diff --git a/tests/test_pbs.py b/tests/test_pbs.py
index f0f7d1203..9319a5a37 100644
--- a/tests/test_pbs.py
+++ b/tests/test_pbs.py
@@ -9,7 +9,6 @@
 
 
 def test_pbs():
-
     """Need to figure out a PBS environment where to run this regression test."""
     if not shutil.which("pbsnodes"):
         pytest.skip("Test runs only on PBS Cluster, must have pbsnodes command")
diff --git a/tests/utils/test_command.py b/tests/utils/test_command.py
index 75ce39d59..c126c2814 100644
--- a/tests/utils/test_command.py
+++ b/tests/utils/test_command.py
@@ -6,7 +6,6 @@
 @pytest.mark.utility
 class TestBuildTestCommand:
     def test_command(self):
-
         cmd = "hostname"
         a = BuildTestCommand(cmd)
         a.execute()
@@ -20,7 +19,6 @@ def test_command(self):
         assert 0 == ret
 
     def test_error_command(self):
-
         cmd = "xyz"
         a = BuildTestCommand(cmd)
         a.execute()
diff --git a/tests/utils/test_file.py b/tests/utils/test_file.py
index 23185e486..f437b8eb6 100644
--- a/tests/utils/test_file.py
+++ b/tests/utils/test_file.py
@@ -3,18 +3,22 @@
 import shutil
 import string
 import tempfile
+import unittest
 import uuid
 
 import pytest
 from buildtest.exceptions import BuildTestError
 from buildtest.utils.file import (
     create_dir,
+    create_file,
     is_dir,
     is_file,
+    is_symlink,
     load_json,
     read_file,
     remove_file,
     resolve_path,
+    search_files,
     walk_tree,
     write_file,
 )
@@ -35,6 +39,41 @@ def test_checking_file():
     assert is_file("/bin/bash")
 
 
+@pytest.mark.utility
+def test_is_symlink():
+    # Target path of the symbolic link
+    link_target = tempfile.NamedTemporaryFile(dir=os.path.expanduser("~"))
+
+    # Symbolic link path
+    link_path = tempfile.NamedTemporaryFile(dir=os.path.expanduser("~"))
+
+    link_path.close()
+
+    # Create symbolic link
+    os.symlink(link_target.name, link_path.name)
+
+    # get filename from the path
+    filename = os.path.basename(link_path.name)
+
+    # test for shell expansion
+    assert is_symlink(os.path.join("$HOME", filename))
+
+    # test for user expansion
+    assert is_symlink(os.path.join("~", filename))
+
+    # test when link is not a symbolic link
+    assert not is_symlink(link_target.name)
+
+    # delete the target file path
+    link_target.close()
+
+    # test for broken symbolic link
+    assert not is_symlink(link_path.name)
+
+    # remove the symbolic link
+    os.remove(link_path.name)
+
+
 @pytest.mark.utility
 def test_directory_expansion():
     dir1 = "$HOME"
@@ -44,6 +83,14 @@ def test_directory_expansion():
     assert is_dir(dir2)
 
 
+def test_create_file():
+    dirname = tempfile.mkdtemp()
+    create_file(os.path.join(dirname, "test.txt"))
+
+    with pytest.raises(BuildTestError):
+        create_file("/xyz.txt")
+
+
 @pytest.mark.utility
 def test_create_dir(tmp_path):
     # since tmp_path creates a directory we will create a subdirectory "test" in tmp_path using create_dir
@@ -60,38 +107,157 @@ def test_create_dir(tmp_path):
         create_dir("/xyz")
 
 
-@pytest.mark.utility
-def test_walk_tree():
-    files = walk_tree(here)
-    assert files
-
-    list_of_files = walk_tree(here, ".py")
-    print(f"Detected {len(list_of_files)} .py files found in directory: {here}")
-    assert len(list_of_files) > 0
-
+class TestWalkTree(unittest.TestCase):
+    def setUp(self):
+        # Create a temporary directory with some files for testing
+        self.tempdir = tempfile.TemporaryDirectory()
+        self.filepaths = [
+            os.path.join(self.tempdir.name, "file1.txt"),
+            os.path.join(self.tempdir.name, "file2.txt"),
+            os.path.join(self.tempdir.name, "file3.doc"),
+            os.path.join(self.tempdir.name, "subdir", "file4.txt"),
+            os.path.join(self.tempdir.name, "subdir", "file5.txt"),
+            os.path.join(self.tempdir.name, "subdir", "file6.doc"),
+            os.path.join(self.tempdir.name, "subdir2", "file7.txt"),
+            os.path.join(self.tempdir.name, "subdir2", "file8.doc"),
+        ]
+        os.makedirs(os.path.join(self.tempdir.name, "subdir"))
+        os.makedirs(os.path.join(self.tempdir.name, "subdir2"))
+        for filepath in self.filepaths:
+            create_file(filepath)
+
+    def test_walk_tree_no_ext(self):
+        result = walk_tree(self.tempdir.name)
+        assert len(result) == 8
+
+    def test_walk_tree_single_ext(self):
+        result = walk_tree(self.tempdir.name, ext=".txt")
+        # check all resulting files have extension .txt
+        for fname in result:
+            assert fname.endswith(".txt")
+
+    def test_walk_tree_multiple_ext(self):
+        result = walk_tree(self.tempdir.name, ext=[".txt", ".doc"])
+        # check all resulting files with extensions .txt and .doc
+        for fname in result:
+            assert fname.endswith(".txt") or fname.endswith(".doc")
+
+    def test_walk_tree_no_files(self):
+        result = walk_tree(self.tempdir.name, ext=".xyz")
+        assert len(result) == 0
+
+    @staticmethod
+    def test_walk_tree_invalid_directory():
+        result = walk_tree("/xyz")
+        assert len(result) == 0
+
+    def test_walk_tree_max_depth(self):
+        result = walk_tree(self.tempdir.name, max_depth=1)
+        assert len(result) == 3
+
+    def test_walk_tree_numfiles(self):
+        result = walk_tree(self.tempdir.name, numfiles=2)
+        assert len(result) == 2
+
+    def test_walk_tree_file_traverse_limit(self):
+        result = walk_tree(self.tempdir.name, file_traverse_limit=6)
+        assert len(result) == 6
+
+    def test_walk_tree_by_directory(self):
+        result = walk_tree(self.tempdir.name, file_type="dir")
+        assert len(result) == 3
+
+    def test_walk_tree_by_symlink(self):
+        os.symlink(self.filepaths[0], os.path.join(self.tempdir.name, "file1.link"))
+        result = walk_tree(self.tempdir.name, file_type="symlink")
+        assert len(result) == 1
+
+        os.symlink(self.filepaths[0], os.path.join(self.tempdir.name, "file1.rst"))
+        os.symlink(self.filepaths[0], os.path.join(self.tempdir.name, "file1.md"))
+        result = walk_tree(self.tempdir.name, file_type="symlink", ext=[".rst", ".md"])
+        assert len(result) == 2
+
+    def tearDown(self):
+        self.tempdir.cleanup()
+
+
+class TestSearchFiles(unittest.TestCase):
+    def setUp(self):
+        self.temp_dir = tempfile.mkdtemp()
+        self.files = [
+            "file1.txt",
+            "file2.jpg",
+            "file3.txt",
+            "file4.jpg",
+            "file5.txt",
+        ]
+        for f in self.files:
+            create_file(os.path.join(self.temp_dir, f))
+
+    def tearDown(self):
+        shutil.rmtree(self.temp_dir)
+
+    def test_search_files_with_pattern(self):
+        # Test searching files with regex pattern
+        result = search_files(self.temp_dir, r"^file\d+\.txt$")
+        assert len(result) == 3
+
+    def test_search_files_with_numfiles(self):
+        # Test searching files with numfiles parameter
+        result = search_files(self.temp_dir, r".*", numfiles=3)
+        assert len(result) == 3
+
+    def test_search_files_with_max_depth(self):
+        # Test searching files with max_depth parameter
+        subdir_path = os.path.join(self.temp_dir, "subdir")
+        os.mkdir(subdir_path)
+
+        create_file(os.path.join(subdir_path, "subfile.txt"))
+
+        result = search_files(subdir_path, r".*", max_depth=1)
+        assert len(result) == 1
+
+    @staticmethod
+    def test_search_files_invalid_directory():
+        result = search_files(root_dir="/xyz", regex_pattern=r".*")
+        print(result)
+        assert len(result) == 0
+
+    @staticmethod
+    def test_search_files_home_dir():
+        # search for files based on variable expansion $HOME
+        result = search_files(
+            root_dir="$HOME", regex_pattern=r".*", file_traverse_limit=10
+        )
+        assert len(result) == 10
+
+        result = search_files(root_dir="~", regex_pattern=r".*", file_traverse_limit=10)
+        assert len(result) == 10
+
+    @staticmethod
+    def test_search_files_invalid_regex():
+        # invalid regular expression will return an empty list
+        files = search_files(here, regex_pattern=r"*foo[1-5]$", max_depth=1)
+        assert len(files) == 0
+
+    def test_search_files_by_symlink(self):
+        # create a symlink to a file
+        os.symlink(
+            os.path.join(self.temp_dir, self.files[0]),
+            os.path.join(self.temp_dir, f"{self.files[0]}.link"),
+        )
+        result = search_files(self.temp_dir, r".*", file_type="symlink")
+        assert len(result) == 1
 
-@pytest.mark.utility
-def test_walk_tree_no_files(tmp_path):
-    # need to convert tmp_path to str since its of type PosixPath
-    list_of_files = walk_tree(str(tmp_path), ".py")
-    print(
-        f"Detected {len(list_of_files)} .py files found in directory: {str(tmp_path)}"
-    )
-    assert 0 == len(list_of_files)
+    def test_search_files_by_directory(self):
+        result = search_files(self.temp_dir, r".*", file_type="dir")
+        assert len(result) == 1
 
+        for subdir in ["subdir1", "subdir2", "subdir3"]:
+            os.mkdir(os.path.join(self.temp_dir, subdir))
 
-@pytest.mark.utility
-def test_walk_tree_invalid_dir(tmp_path):
-    # we want to test an invalid directory so we remove temporary directory created by tmp_path
-    shutil.rmtree(str(tmp_path))
-    print(
-        f"Removing directory: {tmp_path} first before doing directory traversal on invalid directory"
-    )
-    list_of_files = walk_tree(str(tmp_path), ".py")
-    print(
-        f"Returned following files: {list_of_files} with .py extension for path: {tmp_path}"
-    )
-    assert not list_of_files
+        result = search_files(self.temp_dir, r".*", file_type="dir")
+        assert len(result) == 4
 
 
 @pytest.mark.utility
@@ -118,7 +284,6 @@ def test_write_file(tmp_path):
 
 @pytest.mark.utility
 def test_write_file_exceptions(tmp_path):
-
     temporary_directory = str(tmp_path)
     msg = "hi my name is Bob"
     file = os.path.join(temporary_directory, "name.txt")
@@ -221,7 +386,6 @@ def test_load_json():
 
 @pytest.mark.utility
 def test_remove_file():
-
     assert not remove_file(None)
 
     with tempfile.TemporaryDirectory() as tmpdir:
diff --git a/tests/utils/test_shell.py b/tests/utils/test_shell.py
index f57f88fd5..dc30210bf 100755
--- a/tests/utils/test_shell.py
+++ b/tests/utils/test_shell.py
@@ -39,7 +39,6 @@ def test_sh_shell(self):
 
     @pytest.mark.utility()
     def test_bash_shell(self):
-
         shell = Shell("/bin/bash")
         assert shell.name == "/bin/bash"
         assert shell.path == shutil.which("/bin/bash")
@@ -52,7 +51,6 @@ def test_bash_shell(self):
 
     @pytest.mark.utility()
     def test_zsh_shell(self):
-
         if not shutil.which("bin/zsh"):
             pytest.skip("Skipping test for zsh shell")
 
@@ -68,7 +66,6 @@ def test_zsh_shell(self):
 
     @pytest.mark.utility()
     def test_csh_shell(self):
-
         if not shutil.which("bin/csh"):
             pytest.skip("Skipping test for csh shell")
 
@@ -84,7 +81,6 @@ def test_csh_shell(self):
 
     @pytest.mark.utility()
     def test_tcsh_shell(self):
-
         if not shutil.which("bin/tcsh"):
             pytest.skip("Skipping test for tcsh shell")
 
@@ -100,7 +96,6 @@ def test_tcsh_shell(self):
 
     @pytest.mark.utility
     def test_update_instance(self):
-
         # create a sh shell
         shell = Shell("sh")
         assert shell.name == "sh"
diff --git a/tutorials/test_status/exists.yml b/tutorials/test_status/exists.yml
index edfe056f0..80ecac795 100644
--- a/tutorials/test_status/exists.yml
+++ b/tutorials/test_status/exists.yml
@@ -1,18 +1,18 @@
 buildspecs:
   status_exists:
-   type: script
-   executor: generic.local.bash
-   description: status check based for file and directory
-   run: |
+    type: script
+    executor: generic.local.bash
+    description: status check based for file and directory
+    run: |
       mkdir -p $HOME/dirA
       mkdir -p /tmp/ABC
       touch file1
-   status:
-     exists:
-       - $HOME/dirA
-       - ~/.bashrc
-       - /tmp/ABC
-       - file1
+    status:
+      exists:
+        - $HOME/dirA
+        - ~/.bashrc
+        - /tmp/ABC
+        - file1
   status_exists_failure:
     type: script
     executor: generic.local.bash
@@ -20,4 +20,4 @@ buildspecs:
     run: touch foo
     status:
       exists:
-        - bar
\ No newline at end of file
+        - bar
diff --git a/tutorials/test_status/file_and_dir_check.yml b/tutorials/test_status/file_and_dir_check.yml
index 84100df54..d1d38daba 100644
--- a/tutorials/test_status/file_and_dir_check.yml
+++ b/tutorials/test_status/file_and_dir_check.yml
@@ -1,14 +1,14 @@
 buildspecs:
   file_and_dir_checks:
-   type: script
-   executor: generic.local.bash
-   description: status check for files and directories
-   run: hostname
-   status:
-     is_dir:
-       - $HOME
-       - $HOME/.bashrc
-       - /tmp
+    type: script
+    executor: generic.local.bash
+    description: status check for files and directories
+    run: hostname
+    status:
+      is_dir:
+        - $HOME
+        - $HOME/.bashrc
+        - /tmp
   combined_file_and_dir_checks:
     type: script
     executor: generic.local.bash
@@ -19,4 +19,4 @@ buildspecs:
         - $HOME
         - /tmp
       is_file:
-        - $HOME/.bashrc
\ No newline at end of file
+        - $HOME/.bashrc
diff --git a/tutorials/test_status/file_count.yml b/tutorials/test_status/file_count.yml
new file mode 100644
index 000000000..a87d0d661
--- /dev/null
+++ b/tutorials/test_status/file_count.yml
@@ -0,0 +1,29 @@
+buildspecs:
+  file_count_on_directory:
+    type: script
+    executor: generic.local.bash
+    description: file count check in directory
+    run: |
+      mkdir -p foo
+      touch foo/{1..5}
+    status:
+      file_count:
+        - dir: foo
+          count: 5
+  file_count_by_extension:
+    type: script
+    executor: generic.local.bash
+    description: file count by extension
+    run: |
+      mkdir -p foo/bar
+      touch foo/{1..5}.sh
+      touch foo/bar/{1..3}.py foo/bar/{1..3}.txt
+    status:
+      file_count:
+        - dir: foo
+          ext: '.sh'
+          depth: 1
+          count: 5
+        - dir: foo/bar
+          ext: ['.py', '.txt']
+          count: 6
diff --git a/tutorials/test_status/file_count_file_traverse_limit.yml b/tutorials/test_status/file_count_file_traverse_limit.yml
new file mode 100644
index 000000000..d54974140
--- /dev/null
+++ b/tutorials/test_status/file_count_file_traverse_limit.yml
@@ -0,0 +1,16 @@
+buildspecs:
+  file_traverse_limit:
+    type: script
+    executor: generic.local.bash
+    description: Use of file_traverse_limit to limit number of files searched in a directory
+    run: |
+      mkdir foo
+      touch foo/{1..99}.txt
+    status:
+      file_count:
+        - dir: foo
+          count: 50
+          file_traverse_limit: 50
+        - dir: foo
+          count: 10
+          file_traverse_limit: 20
diff --git a/tutorials/test_status/file_count_filetype.yml b/tutorials/test_status/file_count_filetype.yml
new file mode 100644
index 000000000..a385ff13a
--- /dev/null
+++ b/tutorials/test_status/file_count_filetype.yml
@@ -0,0 +1,18 @@
+buildspecs:
+  file_count_by_filetype:
+    type: script
+    executor: generic.local.bash
+    description: Count the number of directories and symbolic links
+    run: |
+      mkdir -p foo/{bar,baz}
+      find foo -type dir
+      ln -s foo/bar foo/bar.link
+      ln -s foo/baz foo/baz.link
+    status:
+      file_count:
+        - dir: foo
+          count: 3
+          filetype: 'dir'
+        - dir: foo
+          count: 2
+          filetype: 'symlink'
diff --git a/tutorials/test_status/file_count_pattern.yml b/tutorials/test_status/file_count_pattern.yml
new file mode 100644
index 000000000..964559892
--- /dev/null
+++ b/tutorials/test_status/file_count_pattern.yml
@@ -0,0 +1,37 @@
+buildspecs:
+  file_count_by_expression:
+    type: script
+    executor: generic.local.bash
+    description: file count by expression
+    run: |
+      mkdir -p /tmp
+      touch /tmp/foo{1..5}.txt
+      ls -l /tmp/foo*.txt
+      filenames=$(find $BUILDTEST_ROOT/buildtest -type f  \( -name "defaults.py" -o -name "main.py" \) -maxdepth 1)
+      totalfiles=$(find $BUILDTEST_ROOT/buildtest -type f  \( -name "defaults.py" -o -name "main.py" \) -maxdepth 1 | wc -l)
+      echo "Filenames: $filenames"
+      echo "Total files: $totalfiles"
+    status:
+      file_count:
+        - dir: /tmp
+          filepattern: 'foo[1-5].txt$'
+          count: 5
+        - dir: '$BUILDTEST_ROOT/buildtest'
+          filepattern: '(defaults|main).py$'
+          count: 2
+          depth: 1
+
+  file_extension_and_filepattern:
+    type: script
+    executor: generic.local.bash
+    description: file count by file extension and file pattern
+    run: |
+      touch foo{1..5}.txt
+      touch {conf,main}.py
+      ls -l
+    status:
+      file_count:
+        - dir: .
+          ext: '.txt'
+          filepattern: '(conf|main).py$'
+          count: 7
diff --git a/tutorials/test_status/is_symlink.yml b/tutorials/test_status/is_symlink.yml
new file mode 100644
index 000000000..1513a7aa2
--- /dev/null
+++ b/tutorials/test_status/is_symlink.yml
@@ -0,0 +1,13 @@
+buildspecs:
+  symlink_test:
+    type: script
+    executor: generic.local.bash
+    description: status check based on symbolic link
+    run: |
+      ln -s /tmp scratch
+      ln -s $HOME/.bashrc $HOME/.bashrc_link
+    status:
+      is_symlink:
+        - scratch
+        - $HOME/.bashrc_link
+        - ~/.bashrc_link
diff --git a/tutorials/test_status/regex_on_filename.yml b/tutorials/test_status/regex_on_filename.yml
new file mode 100644
index 000000000..37ebd501b
--- /dev/null
+++ b/tutorials/test_status/regex_on_filename.yml
@@ -0,0 +1,40 @@
+buildspecs:
+  regex_on_multiple_files:
+    type: script
+    executor: generic.local.bash
+    description: Test regex on multiple files
+    run: |
+      echo "Hello" > hello.txt
+      buildtest --help > buildtest_help.txt
+    status:
+      file_regex:
+        - file: hello.txt
+          exp: '^(Hello)$'
+        - file: buildtest_help.txt
+          exp: '^(usage: buildtest)'
+
+  regex_on_directory_not_supported:
+    type: script
+    executor: generic.local.bash
+    description: Test regex on directory is not supported
+    run: |
+      mkdir -p hello
+      echo "Hello" > hello/hello.txt
+    status:
+      file_regex:
+        - file: hello
+          exp: '^(Hello)$'
+
+  file_expansion_supported:
+    type: script
+    executor: generic.local.bash
+    description: Test regex with variable and shell expansion
+    run: |
+      echo "Hello" > $BUILDTEST_ROOT/hello.txt
+      echo "Hello" > $HOME/hello.txt
+    status:
+      file_regex:
+        - file: $BUILDTEST_ROOT/hello.txt
+          exp: '^(Hello)$'
+        - file: ~/hello.txt
+          exp: '^(Hello)$'