def test_trainer_min_steps_and_min_epochs_not_reached(tmp_path, caplog):
    """Test that min_epochs/min_steps in Trainer are enforced even if EarlyStopping is triggered."""

    class TestModel(BoringModel):
        training_step_invoked = 0

        def training_step(self, batch, batch_idx):
            output = super().training_step(batch, batch_idx)
            output["loss"] = output["loss"] * 0.0  # force minimal loss to trigger early stopping
            self.log("loss", output["loss"])
            self.training_step_invoked += 1
            if self.current_epoch < 2:
                assert not self.trainer.should_stop
            else:
                assert self.trainer.should_stop
            return output

    model = TestModel()
    early_stop = EarlyStopping(monitor="loss", patience=0, check_on_train_epoch_end=True)
    min_epochs = 5
    trainer = Trainer(
        default_root_dir=tmp_path,
        enable_progress_bar=False,
        min_epochs=min_epochs,
        limit_val_batches=0,
        limit_train_batches=2,
        callbacks=[early_stop],
    )
    with caplog.at_level(logging.INFO, logger="lightning.pytorch.trainer.trainer"):
        trainer.fit(model)

    message = f"min_epochs={min_epochs}` or `min_steps=None` has not been met. Training will continue"
    num_messages = sum(1 for record in caplog.records if message in record.message)
    assert num_messages == 1
    assert model.training_step_invoked == min_epochs * 2
Test that min_epochs/min_steps in Trainer are enforced even if EarlyStopping is triggered.
tests/tests_pytorch/trainer/test_trainer.py