pub struct TestContext<'a> {
    pub pack_stdout: String,
    pub pack_stderr: String,
    pub config: BuildConfig,
    /* private fields */
}
Expand description

Context for a currently executing test.

Fields§

§pack_stdout: String

Standard output of pack, interpreted as an UTF-8 string.

§pack_stderr: String

Standard error of pack, interpreted as an UTF-8 string.

§config: BuildConfig

The configuration used for this integration test.

Implementations§

source§

impl<'a> TestContext<'a>

source

pub fn start_container<C: Borrow<ContainerConfig>, F: FnOnce(ContainerContext<'_>)>( &self, config: C, f: F )

Starts a container using the provided ContainerConfig.

If you wish to run a shell command and don’t need to customise the configuration, use the convenience function TestContext::run_shell_command instead.

Examples
use libcnb_test::{BuildConfig, ContainerConfig, TestRunner};

TestRunner::default().build(
    BuildConfig::new("heroku/builder:22", "test-fixtures/app"),
    |context| {
        // Start the container using the default process-type:
        // https://buildpacks.io/docs/app-developer-guide/run-an-app/#default-process-type
        context.start_container(ContainerConfig::new(), |container| {
            // ...
        });

        // Start the container using the specified process-type:
        // https://buildpacks.io/docs/app-developer-guide/run-an-app/#non-default-process-type
        context.start_container(ContainerConfig::new().entrypoint(["worker"]), |container| {
            // ...
        });

        // Start the container using the specified process-type and additional arguments:
        // https://buildpacks.io/docs/app-developer-guide/run-an-app/#non-default-process-type-with-additional-arguments
        context.start_container(
            ContainerConfig::new()
                .entrypoint(["another-process"])
                .command(["--additional-arg"]),
            |container| {
                // ...
            },
        );

        // Start the container using the provided bash script:
        // https://buildpacks.io/docs/app-developer-guide/run-an-app/#user-provided-shell-process-with-bash-script
        // Only use this shell command form if you need to customise the `ContainerConfig`,
        // otherwise use the convenience function `TestContext::run_shell_command` instead.
        context.start_container(
            ContainerConfig::new()
                .entrypoint(["launcher"])
                .command(["for i in {1..3}; do echo \"${i}\"; done"]),
            |container| {
                // ...
            },
        );
    },
);
source

pub fn run_shell_command(&self, command: impl Into<String>) -> LogOutput

Run the provided shell command.

The CNB launcher will run the provided command using bash.

Note: This method will block until the container stops.

Example
use libcnb_test::{BuildConfig, ContainerConfig, TestRunner};

TestRunner::default().build(
    BuildConfig::new("heroku/builder:22", "test-fixtures/app"),
    |context| {
        // ...
        let command_output = context.run_shell_command("for i in {1..3}; do echo \"${i}\"; done");
        assert_eq!(command_output.stdout, "1\n2\n3\n");
    },
);

This is a convenience function for running shell commands inside the image, and is equivalent to:

use libcnb_test::{BuildConfig, ContainerConfig, TestRunner};

TestRunner::default().build(
    BuildConfig::new("heroku/builder:22", "test-fixtures/app"),
    |context| {
        // ...
        context.start_container(
            ContainerConfig::new()
                .entrypoint(["launcher"])
                .command(["for i in {1..3}; do echo \"${i}\"; done"]),
            |container| {
                let log_output = container.logs_wait();
                // ...
            },
        );
    },
);
source

pub fn download_sbom_files<R, F: Fn(SbomFiles) -> R>(&self, f: F) -> R

Downloads SBOM files from the built image into a temporary directory.

References to the downloaded files are passed into the given function and will be cleaned-up after the function exits.

Example
use libcnb_data::sbom::SbomFormat;
use libcnb_test::{BuildConfig, ContainerConfig, SbomType, TestRunner};
use libcnb_data::buildpack_id;

TestRunner::default().build(
    BuildConfig::new("heroku/builder:22", "test-fixtures/app"),
    |context| {
        context.download_sbom_files(|sbom_files| {
            assert!(sbom_files
                .path_for(
                    buildpack_id!("heroku/jvm"),
                    SbomType::Launch,
                    SbomFormat::SyftJson
                )
                .exists());
        });
    },
);
source

pub fn rebuild<C: Borrow<BuildConfig>, F: FnOnce(TestContext<'_>)>( self, config: C, f: F )

Starts a subsequent integration test build.

This function behaves exactly like TestRunner::build, but it will reuse the OCI image from the previous test, causing the CNB lifecycle to restore any cached layers. It will use the same TestRunner as the previous test run.

This function allows testing of subsequent builds, including caching logic and buildpack behaviour when build environment variables change, stacks are upgraded and more.

Note that this function will consume the current context. This is because the image will be changed by the subsequent test, invalidating the context. Running a subsequent test must therefore be the last operation. You can nest subsequent runs if required.

Example
use libcnb_test::{assert_contains, BuildConfig, TestRunner};

TestRunner::default().build(
    BuildConfig::new("heroku/builder:22", "test-fixtures/app"),
    |context| {
        assert_contains!(context.pack_stdout, "---> Installing dependencies");

        let config = context.config.clone();
        context.rebuild(config, |context| {
            assert_contains!(context.pack_stdout, "---> Using cached dependencies");
        });
    },
);

Trait Implementations§

source§

impl<'a> Drop for TestContext<'a>

source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

§

impl<'a> !RefUnwindSafe for TestContext<'a>

§

impl<'a> !Send for TestContext<'a>

§

impl<'a> !Sync for TestContext<'a>

§

impl<'a> Unpin for TestContext<'a>

§

impl<'a> !UnwindSafe for TestContext<'a>

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more