コード例 #1
0
ファイル: test_wrap.py プロジェクト: goern/thoth-adviser
    def test_run_log(self, caplog, context: Context, state: State,
                     log_level: str) -> None:
        """Check logging messages."""
        prescription_str = f"""
name: WrapUnit
type: wrap
should_include:
  times: 1
  adviser_pipeline: true
match:
 state:
    resolved_dependencies:
      - name: flask
run:
  log:
    message: Seen flask in one of the resolved stacks
    type: {log_level}
"""
        prescription = yaml.safe_load(prescription_str)
        PRESCRIPTION_WRAP_SCHEMA(prescription)
        WrapPrescription.set_prescription(prescription)
        state.add_resolved_dependency(
            ("flask", "0.12", "https://pypi.org/simple"))
        self.check_run_log(caplog,
                           context,
                           log_level,
                           WrapPrescription,
                           state=state)
コード例 #2
0
    def test_run_no_resolved(self, context: Context, state: State) -> None:
        """Test running this pipeline unit not matching any resolved dependency."""
        prescription_str = """
name: GitHubReleaseNotes
type: wrap.GitHubReleaseNotes
should_include:
  adviser_pipeline: true
run:
  release_notes:
    - organization: thoth-station
      repository: adviser
      tag_version_prefix: v
      package_version:
        name: adviser
"""
        prescription = yaml.safe_load(prescription_str)
        PRESCRIPTION_GITHUB_RELEASE_NOTES_WRAP_SCHEMA(prescription)
        GitHubReleaseNotesWrapPrescription.set_prescription(prescription)

        state.resolved_dependencies.clear()
        state.add_resolved_dependency(
            ("flask", "1.0.0", "https://pypi.org/simple"))
        state.justification.clear()

        unit = GitHubReleaseNotesWrapPrescription()
        unit.pre_run()
        with unit.assigned_context(context):
            assert unit.run(state) is None

        assert state.justification == []
コード例 #3
0
    def test_run_not_acceptable(self, context: Context, tf_name: str,
                                np_version: str) -> None:
        """Test wrong resolutions are not acceptable."""
        package_version = PackageVersion(
            name="numpy",
            version=f"=={np_version}",
            develop=False,
            index=Source("https://pypi.org/simple"),
        )

        state = State()
        state.add_resolved_dependency(
            (tf_name, "1.13.1", "https://pypi.org/simple"))

        unit = TensorFlow113NumPyStep()
        unit.pre_run()

        assert not context.stack_info

        with unit.assigned_context(context):
            with pytest.raises(NotAcceptable):
                assert unit._message_logged is False
                unit.run(state, package_version)

        assert unit._message_logged is True
        assert context.stack_info
        assert self.verify_justification_schema(context.stack_info) is True
コード例 #4
0
ファイル: test_stride.py プロジェクト: goern/thoth-adviser
    def test_run_not_acceptable(self, context: Context, state: State) -> None:
        """Check raising not acceptable."""
        prescription_str = """
name: StrideUnit
type: stride
should_include:
  times: 1
  adviser_pipeline: true
match:
  state:
    resolved_dependencies:
      - name: flask
        version: "<=1.0.0,>=0.12"
        index_url: "https://pypi.org/simple"
      - name: connexion
        version: "==2.7.0"
        index_url: "https://pypi.org/simple"
run:
  not_acceptable: This is exception message reported
"""
        prescription = yaml.safe_load(prescription_str)
        PRESCRIPTION_STRIDE_SCHEMA(prescription)
        StridePrescription.set_prescription(prescription)
        state.add_resolved_dependency(("flask", "0.12", "https://pypi.org/simple"))
        state.add_resolved_dependency(("connexion", "2.7.0", "https://pypi.org/simple"))
        self.check_run_not_acceptable(context, StridePrescription, state=state)
コード例 #5
0
    def test_run_not_acceptable(self, context: Context, tf_name: str,
                                tf_version: str, np_version: str) -> None:
        """Test resolutions that are not acceptable."""
        package_version = PackageVersion(
            name="numpy",
            version=f"=={np_version}",
            develop=False,
            index=Source("https://pypi.org/simple"),
        )

        tf_package_version = PackageVersion(
            name=tf_name,
            version=f"=={tf_version}",
            develop=False,
            index=Source("https://pypi.org/simple"),
        )

        state = State()
        state.add_resolved_dependency(tf_package_version.to_tuple())
        context.register_package_version(tf_package_version)

        unit = TensorFlow22NumPyStep()
        unit.pre_run()

        with unit.assigned_context(context):
            assert unit._message_logged is False
            with pytest.raises(NotAcceptable):
                unit.run(state, package_version)

        assert unit._message_logged is True
コード例 #6
0
    def test_run_no_resolved(self, context: Context, state: State) -> None:
        """Test running this pipeline unit not matching any resolved dependency."""
        prescription_str = """
name: GHReleaseNotes
type: wrap.GHReleaseNotes
should_include:
  adviser_pipeline: true
match:
  state:
    resolved_dependencies:
      - name: numpy
run:
  release_notes:
    organization: thoth-station
    repository: adviser
    tag_version_prefix: v
"""
        units = list(self._instantiate_gh_release_notes_wrap(prescription_str))
        assert len(
            units
        ) == 1, "Multiple units created, expected only one based on prescription"
        unit = units[0]

        state.resolved_dependencies.clear()
        state.add_resolved_dependency(
            ("flask", "1.0.0", "https://pypi.org/simple"))
        state.justification.clear()

        unit.pre_run()
        with unit.assigned_context(context):
            assert unit.run(state) is None

        assert state.justification == []
コード例 #7
0
    def test_environment_gpu_cuda_version(
        self,
        cuda_version: Optional[str],
        trove_classifiers: List[str],
        context: Context,
        state: State,
        justification: List[Dict[str, str]],
    ) -> None:
        """Test adding justifications for environment GPU CUDA version."""
        package_name = "tensorflow-gpu"
        package_version = "2.6.0"
        index_url = "https://pypi.org/simple"

        context.project.runtime_environment.cuda_version = cuda_version
        context.graph.should_receive("get_python_package_version_trove_classifiers_all").with_args(
            package_name=package_name,
            package_version=package_version,
            index_url=index_url,
            os_name=context.project.runtime_environment.operating_system.name,
            os_version=context.project.runtime_environment.operating_system.version,
            python_version=context.project.runtime_environment.python_version,
        ).and_return(trove_classifiers).once()

        state.justification.clear()

        unit = self.UNIT_TESTED()
        unit.pre_run()

        state.resolved_dependencies.clear()
        state.add_resolved_dependency((package_name, package_version, index_url))

        with unit.assigned_context(context):
            unit.run(state)

        assert state.justification == justification
コード例 #8
0
    def test_development_status(
        self,
        development_status_classifiers: List[str],
        recommendation_type: RecommendationType,
        justification: List[Dict[str, str]],
        state: State,
        context: Context,
    ) -> None:
        """Test adding justifications for development status."""
        package_name = "thoth-common"
        package_version = "0.26.0"
        index_url = "https://pypi.org/simple"

        context.graph.should_receive("get_python_package_version_trove_classifiers_all").with_args(
            package_name=package_name,
            package_version=package_version,
            index_url=index_url,
            os_name=context.project.runtime_environment.operating_system.name,
            os_version=context.project.runtime_environment.operating_system.version,
            python_version=context.project.runtime_environment.python_version,
        ).and_return(development_status_classifiers).once()
        context.recommendation_type = recommendation_type

        state.justification.clear()

        unit = self.UNIT_TESTED()
        unit.pre_run()

        state.resolved_dependencies.clear()
        state.add_resolved_dependency((package_name, package_version, index_url))

        with unit.assigned_context(context):
            unit.run(state)

        assert state.justification == justification
コード例 #9
0
    def test_run_noop(self, context: Context, tf_name: str, tf_version: str,
                      gast_version: str) -> None:
        """Test no operation performed when not invalid combination is seen."""
        gast_package_version = PackageVersion(
            name="gast",
            version=f"=={gast_version}",
            develop=False,
            index=Source("https://pypi.org/simple"),
        )
        tf_package_version = PackageVersion(
            name=tf_name,
            version=f"=={tf_version}",
            develop=False,
            index=Source("https://pypi.org/simple"),
        )

        state = State()
        state.add_resolved_dependency(tf_package_version.to_tuple())
        context.register_package_version(tf_package_version)

        unit = TensorFlow114GastStep()

        with unit.assigned_context(context):
            assert unit._message_logged is False
            assert unit.run(state, gast_package_version) is None
            assert unit._message_logged is False
コード例 #10
0
    def test_run_not_acceptable(self, context: Context, tf_name: str,
                                tf_version: str, gast_version: str) -> None:
        """Test not acceptable TensorFlow<=1.14 with gast>0.2.2."""
        gast_package_version = PackageVersion(
            name="gast",
            version=f"=={gast_version}",
            develop=False,
            index=Source("https://pypi.org/simple"),
        )
        tf_package_version = PackageVersion(
            name=tf_name,
            version=f"=={tf_version}",
            develop=False,
            index=Source("https://pypi.org/simple"),
        )

        state = State()
        state.add_resolved_dependency(tf_package_version.to_tuple())
        context.register_package_version(tf_package_version)

        unit = TensorFlow114GastStep()

        with unit.assigned_context(context):
            assert unit._message_logged is False
            with pytest.raises(NotAcceptable):
                unit.run(state, gast_package_version)

        assert unit._message_logged is True
コード例 #11
0
ファイル: test_td.py プロジェクト: KPostOffice/adviser
    def test_set_reward_signal_nan_inf(self, float_case: float) -> None:
        """Test (not) keeping the reward signal for nan/inf."""
        predictor = TemporalDifference()
        state = State()
        state.add_resolved_dependency(
            ("tensorflow", "2.3.0", "https://pypi.org/simple"))
        state.add_resolved_dependency(
            ("flask", "0.12", "https://pypi.org/simple"))
        state.add_unresolved_dependency(
            ("termial-random", "0.0.2", "https://pypi.org/simple"))
        predictor._policy = {
            ("flask", "0.12", "https://pypi.org/simple"): [0.2, 1],
        }
        predictor._steps_taken = 2
        predictor._steps_reward = 1.2
        predictor._next_state = state

        assert (predictor.set_reward_signal(
            state, ("tensorflow", "2.0.0", "https://pypi.org/simple"),
            float_case) is None)

        assert predictor._policy == {
            ("flask", "0.12", "https://pypi.org/simple"): [1.4, 2],
            ("tensorflow", "2.3.0", "https://pypi.org/simple"): [1.2, 1],
        }
        assert predictor._steps_taken == 0
        assert predictor._steps_reward == 0.0
        assert predictor._next_state is None
コード例 #12
0
    def test_run_acceptable_tf(self) -> None:
        """Test noop for this pipeline unit."""
        package_version_1 = PackageVersion(
            name="tensorflow-probability",
            version="==0.11.0",
            develop=False,
            index=Source("https://pypi.org/simple"),
        )

        package_version_2 = PackageVersion(
            name="flask",
            version="==0.12",
            develop=False,
            index=Source("https://pypi.org/simple"),
        )

        state = State()
        unit = TensorFlow22ProbabilityStep()

        assert unit.run(state, package_version_1) is None
        assert unit.run(state, package_version_2) is None

        state.add_resolved_dependency(("tensorflow", "2.3.0", "https://pypi.org/simple"))
        assert unit.run(state, package_version_1) is None
        assert unit.run(state, package_version_2) is None
コード例 #13
0
    def test_run(self, context: Context, package_name: str) -> None:
        """Test recommending not to use TensorFlow 2.2 with tensorflow-probability."""
        package_version = PackageVersion(
            name="tensorflow-probability",
            version="==0.11.0",
            develop=False,
            index=Source("https://pypi.org/simple"),
        )

        state = State()
        state.add_resolved_dependency((package_name, "2.2.0", "https://pypi.org/simple"))

        unit = TensorFlow22ProbabilityStep()
        unit.pre_run()

        assert not context.stack_info

        with unit.assigned_context(context):
            with pytest.raises(NotAcceptable):
                assert unit._message_logged is False
                unit.run(state, package_version)
                assert unit._message_logged is True

        assert context.stack_info
        assert self.verify_justification_schema(context.stack_info) is True
コード例 #14
0
ファイル: test_wrap.py プロジェクト: goern/thoth-adviser
    def test_run_stack_info(self, context: Context, state: State) -> None:
        """Check assigning stack info."""
        prescription_str = """
name: WrapUnit
type: wrap
should_include:
  times: 1
  adviser_pipeline: true
match:
 state:
    resolved_dependencies:
      - name: werkzeug
        version: "<=1.0.0"
        index_url: 'https://pypi.org/simple'
run:
  stack_info:
    - type: WARNING
      message: Some message
      link: https://pypi.org/project/werkzeug
"""
        prescription = yaml.safe_load(prescription_str)
        PRESCRIPTION_WRAP_SCHEMA(prescription)
        WrapPrescription.set_prescription(prescription)
        state.add_resolved_dependency(
            ("werkzeug", "0.5.0", "https://pypi.org/simple"))
        self.check_run_stack_info(context, WrapPrescription, state=state)
コード例 #15
0
    def test_run_justification_noop(self) -> None:
        """Test no operation when PyTorch is not present."""
        state = State()
        state.add_resolved_dependency(("micropipenv", "0.1.4", "https://pypi.org/simple"))
        assert not state.justification

        unit = MKLThreadsWrap()
        unit.run(state)

        assert len(state.justification) == 0
コード例 #16
0
    def test_run_no_justification(self) -> None:
        """Test NOT adding information if the given Python package is not a PyPI release."""
        state = State()
        state.add_resolved_dependency(
            ("tensorflow", "2.5.0", "https://thoth-station.ninja"))

        unit = self.UNIT_TESTED()
        unit.run(state)

        assert not state.justification
コード例 #17
0
    def test_run_no_justification(self) -> None:
        """Test NOT adding a link to the PyTorch index."""
        state = State()
        state.add_resolved_dependency(
            ("torch", "1.10.2", "https://pypi.org/simple"))

        unit = self.UNIT_TESTED()
        unit.run(state)

        assert not state.justification
コード例 #18
0
    def test_run_develop(self, context: Context, state: State,
                         develop: bool) -> None:
        """Test running this pipeline unit resulting in a justification addition when "not" index url is used."""
        prescription_str = f"""
name: GHReleaseNotes
type: wrap.GHReleaseNotes
should_include:
  adviser_pipeline: true
match:
  state:
    resolved_dependencies:
      name: thoth-solver
      develop: {'true' if develop else 'false'}
run:
  release_notes:
    organization: thoth-station
    repository: solver
    tag_version_prefix: v
"""
        units = list(self._instantiate_gh_release_notes_wrap(prescription_str))
        assert len(
            units
        ) == 1, "Multiple units created, expected only one based on prescription"
        unit = units[0]

        state.resolved_dependencies.clear()
        state.justification.clear()

        package_version = PackageVersion(
            name="thoth-solver",
            version="==0.5.0",
            index=Source("https://pypi.org/simple"),
            develop=develop,
        )
        state.add_resolved_dependency(package_version.to_tuple())
        context.register_package_version(package_version)

        unit.pre_run()
        with unit.assigned_context(context):
            # Run twice to verify the justification is added just once.
            assert unit.run(state) is None
            assert unit.run(state) is None

        self.verify_justification_schema(state.justification)
        assert set(tuple(i.items()) for i in state.justification) == {
            (
                ("type", "INFO"),
                ("message", "Release notes for package 'thoth-solver'"),
                ("link",
                 "https://github.com/thoth-station/solver/releases/tag/v0.5.0"
                 ),
                ("package_name", "thoth-solver"),
            ),
        }
コード例 #19
0
    def test_run_noop(self) -> None:
        """Test no justification added if TensorFlow 2.3 is not resolved."""
        state = State()
        assert not state.justification
        assert "tensorflow" not in state.resolved_dependencies

        state.add_resolved_dependency(("tensorflow", "2.2.0", "https://pypi.org/simple"))

        unit = TensorFlow23DictSummary()
        unit.run(state)

        assert len(state.justification) == 0
コード例 #20
0
    def test_run_no_justification(self) -> None:
        """Test NOT adding a link to Pulp instance for the given package released."""
        index_url = "https://pypi.org/simple"
        assert not self.UNIT_TESTED._PULP_URL.startswith(index_url)

        state = State()
        state.add_resolved_dependency(("tensorflow", "2.5.0", index_url))

        unit = self.UNIT_TESTED()
        unit.run(state)

        assert not state.justification
コード例 #21
0
ファイル: test_td.py プロジェクト: KPostOffice/adviser
    def test_n_step_td_step_adjust(self, context: Context) -> None:
        """Test adjusting steps taken on reward signal propagation."""
        predictor = TemporalDifference(step=1)
        predictor._temperature = 1.0
        predictor._steps_taken = 1
        package_tuple = ("tensorflow", "2.3.1", "https://pypi.org/simple")
        state = State()
        state.add_resolved_dependency(package_tuple)
        with predictor.assigned_context(context):
            predictor.set_reward_signal(state, package_tuple, 0.33)

        assert predictor._policy.get(package_tuple) == [0.33, 1]
        assert predictor._steps_taken == 0
コード例 #22
0
ファイル: test_step.py プロジェクト: goern/thoth-adviser
    def test_run_develop_state_match(self, context: Context, state: State,
                                     develop: bool,
                                     state_develop: bool) -> None:
        """Test not running the prescription if develop flag is set also on the state match."""
        prescription_str = f"""
name: StepUnit
type: step
should_include:
  times: 1
  adviser_pipeline: true
match:
  package_version:
    name: numpy
    develop: {'true' if develop else 'false'}
  state:
    resolved_dependencies:
    - name: pytest
      develop: {'true' if state_develop else 'false'}
run:
  score: 0.5
"""
        prescription = yaml.safe_load(prescription_str)
        PRESCRIPTION_STEP_SCHEMA(prescription)
        StepPrescription.set_prescription(prescription)
        package_version = PackageVersion(
            name="numpy",
            version="==1.19.1",
            index=Source("https://pypi.org/simple"),
            develop=develop,
        )

        state_package_version = PackageVersion(
            name="pytest",
            version="==6.2.4",
            index=Source("https://pypi.org/simple"),
            develop=state_develop,
        )
        state.add_resolved_dependency(state_package_version.to_tuple())
        context.register_package_version(state_package_version)

        unit = StepPrescription()
        unit.pre_run()
        with unit.assigned_context(context):
            result = unit.run(state, package_version)

        assert isinstance(result, tuple)
        assert result[0] == 0.5
        assert result[1] is None
コード例 #23
0
    def test_run_add_justification(self) -> None:
        """Test adding link to libraries.io about the given package."""
        state = State()
        state.add_resolved_dependency(
            ("tensorflow", "2.5.0", "https://pypi.org/simple"))

        unit = self.UNIT_TESTED()
        unit.run(state)

        assert state.justification == [{
            "type": "INFO",
            "message": "Information about 'tensorflow' on libraries.io",
            "link": "https://libraries.io/pypi/tensorflow/",
            "package_name": "tensorflow",
        }]
        self.verify_justification_schema(state.justification)
コード例 #24
0
    def test_run_add_justification(self) -> None:
        """Test adding information Intel's MKL environment variable."""
        state = State()
        state.add_resolved_dependency(("pytorch", "1.4.0", "https://pypi.org/simple"))
        assert len(state.advised_manifest_changes) == 0
        assert not state.justification

        unit = MKLThreadsWrap()
        unit.run(state)

        assert len(state.justification) == 1
        assert set(state.justification[0].keys()) == {"type", "message", "link"}
        assert state.justification[0]["type"] == "WARNING"
        assert state.justification[0]["link"], "Empty link to justification document provided"

        self._check_advised_manifest_changes(state)
コード例 #25
0
    def test_run(self, tf_version: str) -> None:
        """Test adding justification added if TensorFlow 2.3 is resolved."""
        state = State()
        assert not state.justification
        assert "tensorflow" not in state.resolved_dependencies

        state.add_resolved_dependency(("tensorflow", tf_version, "https://pypi.org/simple"))

        unit = TensorFlow23DictSummary()
        unit.run(state)

        assert len(state.justification) == 1
        assert set(state.justification[0].keys()) == {"type", "message", "link"}
        assert state.justification[0]["type"] == "WARNING"
        assert state.justification[0]["message"], "No justification message provided"
        assert state.justification[0]["link"], "Empty link to justification document provided"
コード例 #26
0
ファイル: test_wrap.py プロジェクト: tlegen-k/adviser
    def test_advised_manifest_changes(self, state: State, context: Context) -> None:
        """Test advising changes in the manifest files."""
        prescription_str = """
name: WrapUnit
type: wrap
should_include:
  times: 1
  adviser_pipeline: true
match:
  state:
    resolved_dependencies:
      - name: intel-tensorflow
run:
  advised_manifest_changes:
    apiVersion: apps.openshift.io/v1
    kind: DeploymentConfig
    patch:
      op: add
      path: /spec/template/spec/containers/0/env/0
      value:
        name: OMP_NUM_THREADS
        value: "1"
"""
        prescription = yaml.safe_load(prescription_str)
        PRESCRIPTION_WRAP_SCHEMA(prescription)
        WrapPrescription.set_prescription(prescription)
        state.add_resolved_dependency(("intel-tensorflow", "2.2.0", "https://pypi.org/simple"))

        state.justification.clear()
        assert state.advised_manifest_changes == []

        unit = WrapPrescription()
        unit.pre_run()
        with unit.assigned_context(context):
            assert unit.run(state) is None

        assert state.advised_manifest_changes == [
            {
                "apiVersion": "apps.openshift.io/v1",
                "kind": "DeploymentConfig",
                "patch": {
                    "op": "add",
                    "path": "/spec/template/spec/containers/0/env/0",
                    "value": {"name": "OMP_NUM_THREADS", "value": "1"},
                },
            }
        ]
コード例 #27
0
    def test_run_add_justification(self, context: Context,
                                   state: State) -> None:
        """Test running this pipeline unit resulting in a justification addition."""
        prescription_str = """
name: GHReleaseNotes
type: wrap.GHReleaseNotes
should_include:
  adviser_pipeline: true
match:
  - state:
     resolved_dependencies:
       - name: thoth-adviser
run:
  release_notes:
    organization: thoth-station
    repository: adviser
    tag_version_prefix: v
"""
        units = list(self._instantiate_gh_release_notes_wrap(prescription_str))
        assert len(
            units
        ) == 1, "Multiple units created, expected only one based on prescription"
        unit = units[0]

        state.resolved_dependencies.clear()
        state.add_resolved_dependency(
            ("flask", "1.0.0", "https://pypi.org/simple"))
        state.add_resolved_dependency(
            ("thoth-adviser", "1.0.0", "https://pypi.org/simple"))
        state.justification.clear()

        unit.pre_run()
        with unit.assigned_context(context):
            assert unit.run(state) is None

        self.verify_justification_schema(state.justification)
        assert set(tuple(i.items()) for i in state.justification) == {
            (
                ("type", "INFO"),
                ("message", "Release notes for package 'thoth-adviser'"),
                ("link",
                 "https://github.com/thoth-station/adviser/releases/tag/v1.0.0"
                 ),
                ("package_name", "thoth-adviser"),
            ),
        }
コード例 #28
0
    def test_run(self) -> None:
        """Test running this stride to filter same stacks."""
        context = flexmock()

        state = State()
        state.add_resolved_dependency(("tensorflow", "2.2.0", "https://pypi.org/simple"))

        unit = UniqueStackStride()
        with unit.assigned_context(context):
            assert unit.run(state) is None

            # Running the stride on the same stack should cause rejecting it.
            with pytest.raises(NotAcceptable):
                unit.run(state)

            # A stack with another package should be included.
            state.add_resolved_dependency(("numpy", "1.19.1", "https://pypi.org/simple"))
            assert unit.run(state) is None
コード例 #29
0
    def test_run_noop(self, tf_name: str, np_version: str) -> None:
        """Test wrong resolutions are not acceptable."""
        package_version = PackageVersion(
            name="numpy",
            version=f"=={np_version}",
            develop=False,
            index=Source("https://pypi.org/simple"),
        )

        state = State()
        state.add_resolved_dependency(
            (tf_name, "1.13.1", "https://pypi.org/simple"))

        unit = TensorFlow113NumPyStep()

        assert unit._message_logged is False
        assert unit.run(state, package_version) is None
        assert unit._message_logged is False
コード例 #30
0
ファイル: test_stride.py プロジェクト: goern/thoth-adviser
    def test_run_match_develop(self, context: Context, state: State, develop: bool) -> None:
        """Test running this pipeline unit based on develop matching."""
        prescription_str = f"""
name: StrideUnit
type: stride
should_include:
  times: 1
  adviser_pipeline: true
match:
  state:
    resolved_dependencies:
      - name: flask
        develop: {'true' if develop else 'false'}
run:
  stack_info:
    - type: INFO
      message: This message will be shown
      link: https://thoth-station.ninja
"""
        prescription = yaml.safe_load(prescription_str)
        PRESCRIPTION_STRIDE_SCHEMA(prescription)
        StridePrescription.set_prescription(prescription)
        package_version = PackageVersion(
            name="flask",
            version="==2.0.1",
            index=Source("https://pypi.org/simple"),
            develop=develop,
        )
        state.add_resolved_dependency(package_version.to_tuple())
        context.register_package_version(package_version)

        assert not context.stack_info

        unit = StridePrescription()
        unit.pre_run()
        with unit.assigned_context(context):
            assert unit.run(state) is None

            # Run one more time to verify the stack info is added just once.
            assert unit.run(state) is None

        assert context.stack_info == [
            {"type": "INFO", "message": "This message will be shown", "link": "https://thoth-station.ninja"}
        ]