Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1154,3 +1154,6 @@ if (GITHUB_ACTIONS_BUILD)
fsdb_all_services
)
endif()

# FBOSS Image Builder distro_cli tests
include(cmake/FbossImageDistroCliTests.cmake)
2 changes: 2 additions & 0 deletions build/fbcode_builder/CMake/FBPythonBinary.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ function(add_fb_python_executable TARGET)
# CMake doesn't really seem to like having a directory specified as an
# output; specify the __main__.py file as the output instead.
set(zipapp_output_file "${zipapp_output}/__main__.py")
# Update output_file to match zipapp_output_file for dir type
set(output_file "${zipapp_output_file}")
list(APPEND
extra_cmd_params
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${zipapp_output}"
Expand Down
92 changes: 92 additions & 0 deletions cmake/FbossImageDistroCliTests.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# CMake to build and test fboss-image/distro_cli

# In general, libraries and binaries in fboss/foo/bar are built by
# cmake/FooBar.cmake

# distro_cli requires Python 3.10+ with widespread use of union type syntax
# Save and temporarily override Python3_EXECUTABLE
if(DEFINED Python3_EXECUTABLE)
set(SAVED_Python3_EXECUTABLE "${Python3_EXECUTABLE}")
endif()

# Find the highest available Python version >= 3.10
find_package(Python3 3.10 COMPONENTS Interpreter REQUIRED)
message(STATUS "Using Python ${Python3_VERSION} (${Python3_EXECUTABLE}) for distro_cli tests")

include(FBPythonBinary)

file(GLOB DISTRO_CLI_TEST_SOURCES
"fboss-image/distro_cli/tests/*_test.py"
)

# Exclude image_builder_test.py - requires source tree access for templates
# which isn't available in CMake build directory
list(FILTER DISTRO_CLI_TEST_SOURCES EXCLUDE REGEX "image_builder_test\\.py$")

# Exclude: manual e2e tests
list(FILTER DISTRO_CLI_TEST_SOURCES EXCLUDE REGEX "kernel_build_test\\.py$")
list(FILTER DISTRO_CLI_TEST_SOURCES EXCLUDE REGEX "sai_build_test\\.py$")

# Exclude: Docker not available
list(FILTER DISTRO_CLI_TEST_SOURCES EXCLUDE REGEX "build_entrypoint_test\\.py$")
list(FILTER DISTRO_CLI_TEST_SOURCES EXCLUDE REGEX "build_test\\.py$")
list(FILTER DISTRO_CLI_TEST_SOURCES EXCLUDE REGEX "docker_test\\.py$")

file(GLOB DISTRO_CLI_TEST_HELPERS
"fboss-image/distro_cli/tests/test_helpers.py"
)

file(GLOB_RECURSE DISTRO_CLI_LIB_SOURCES
"fboss-image/distro_cli/builder/*.py"
"fboss-image/distro_cli/cmds/*.py"
"fboss-image/distro_cli/lib/*.py"
"fboss-image/distro_cli/tools/*.py"
)

# Create Python unittest executable with test data files
# Use TYPE "dir" to create a directory-based executable instead of zipapp.
# This allows tests to access data files via Path(__file__).parent, which
# doesn't work inside zip archives.
#
# Note: Only include .py files in SOURCES. Non-Python data files would be
# treated as Python modules during test discovery, causing import errors.
# Data files are copied via add_custom_command below after the build
# generates the directory structure.
add_fb_python_unittest(
distro_cli_tests
BASE_DIR "fboss-image"
TYPE "dir"
SOURCES
${DISTRO_CLI_TEST_SOURCES}
${DISTRO_CLI_TEST_HELPERS}
${DISTRO_CLI_LIB_SOURCES}
ENV
"PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/fboss-image"
)

# Copy test data files AFTER the build generates the directory structure
# Tests access these via Path(__file__).parent / "data" / "filename"
# With TYPE "dir", the executable is created at distro_cli_tests/ so data
# files need to be inside that directory structure.
#
# We use add_custom_command to copy AFTER the .GEN_PY_EXE target runs,
# because that target creates/recreates the distro_cli_tests/ directory.
set(DATA_DEST_DIR "${CMAKE_CURRENT_BINARY_DIR}/distro_cli_tests/distro_cli/tests")
set(DATA_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/fboss-image/distro_cli/tests/data")

add_custom_command(
TARGET distro_cli_tests.GEN_PY_EXE
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${DATA_SOURCE_DIR}"
"${DATA_DEST_DIR}/data"
COMMENT "Copying test data files for distro_cli_tests"
)

install_fb_python_executable(distro_cli_tests)

# Restore the original Python3_EXECUTABLE if it was set
if(DEFINED SAVED_Python3_EXECUTABLE)
set(Python3_EXECUTABLE "${SAVED_Python3_EXECUTABLE}")
unset(SAVED_Python3_EXECUTABLE)
endif()
68 changes: 64 additions & 4 deletions fboss-image/distro_cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,20 +110,56 @@ Components are built in the following order:

### Running Tests

#### With Bazel (Recommended)

```bash
# Run all tests
cd fboss-image/distro_cli
python3 -m unittest discover -s tests -p '*_test.py'
bazel test //fboss/fboss-image/distro_cli:all

# Run specific test
python3 -m unittest tests.cli_test
bazel test //fboss/fboss-image/distro_cli:cli_test

# Run with detailed output
bazel test //fboss/fboss-image/distro_cli:all --test_output=all
```

#### With pytest

```bash
# Run all tests (from fboss-image directory)
cd fboss-image
PYTHONPATH=. python3 -m pytest distro_cli/tests/ -v

# Run specific test file
PYTHONPATH=. python3 -m pytest distro_cli/tests/cli_test.py -v

# Run specific test class or method
PYTHONPATH=. python3 -m pytest distro_cli/tests/cli_test.py::CLITest::test_cli_creation -v
```

#### With unittest

```bash
# Run all tests (from fboss-image directory)
cd fboss-image
PYTHONPATH=. python3 -m unittest discover -s distro_cli/tests -p '*_test.py'

# Run specific test module
PYTHONPATH=. python3 -m unittest distro_cli.tests.cli_test

# Run specific test class
PYTHONPATH=. python3 -m unittest distro_cli.tests.cli_test.CLITest
```

### Linting

```bash
# With ruff (from distro_cli directory)
cd fboss-image/distro_cli
python3 -m ruff check .

# With bazel (from repository root)
bazel test //private-fboss/fboss-image/distro_cli:lint_check
```

### Project Structure
Expand Down Expand Up @@ -152,7 +188,7 @@ fboss-image/distro_cli/
The CLI uses a custom OOP wrapper around argparse (stdlib only, no external dependencies):

```python
from lib.cli import CLI
from distro_cli.lib.cli import CLI

# Create CLI
cli = CLI(description='My CLI')
Expand All @@ -171,3 +207,27 @@ device.add_command('ssh', ssh_func, help_text='SSH to device')
# Run
cli.run(setup_logging_func=setup_logging)
```

## Running Tests

### Quick Tests (Default)
Run all fast unit tests (excludes long-running E2E tests):
```bash
python3 -m pytest distro_cli/tests/ -v
# or explicitly exclude e2e tests:
python3 -m pytest distro_cli/tests/ -m "not e2e" -v
```

### E2E Tests Only
Run only the end-to-end tests (kernel build, SAI build):
```bash
python3 -m pytest distro_cli/tests/ -m e2e -v -s
```

### All Tests (Including E2E)
Run everything:
```bash
python3 -m pytest distro_cli/tests/ -v -s
```

**Note:** E2E tests can take 10-60 minutes and require actual source files (kernel sources, SAI SDK, etc.).
8 changes: 8 additions & 0 deletions fboss-image/distro_cli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2004-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.

"""FBOSS Distribution CLI package."""
8 changes: 8 additions & 0 deletions fboss-image/distro_cli/builder/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2004-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.

"""FBOSS Distribution CLI builder package."""
Loading