def test_publish_artifacts():
    stream = StreamStub()
    messages = TeamcityServiceMessages(output=stream, now=lambda: fixed_date)
    messages.publishArtifacts('/path/to/file')
    assert stream.observed_output.strip() == textwrap.dedent("""\
        ##teamcity[publishArtifacts '/path/to/file']
        """).strip().encode('utf-8')
def test_publish_artifacts():
    stream = StreamStub()
    messages = TeamcityServiceMessages(output=stream, now=lambda: fixed_date)
    messages.publishArtifacts('/path/to/file')
    assert stream.observed_output.strip() == textwrap.dedent("""\
        ##teamcity[publishArtifacts '/path/to/file']
        """).strip().encode('utf-8')
class TeamcityBuild(UserBuild):
    def __init__(self, configuration):
        super().__init__(configuration)

        # This accounts for the volume mapping from the container.
        # Our local /results is mapped to some relative ./results on the host,
        # so we use /results/artifacts to copy our files but results/artifacts as
        # an artifact path for teamcity.
        # TODO abstract out the volume mapping
        self.artifact_dir = Path("/results/artifacts")

        self.teamcity_messages = TeamcityServiceMessages()

    def copy_artifacts(self, artifacts):
        super().copy_artifacts(artifacts)

        # Start loading the junit reports.
        junit_reports_pattern = "{}/junit/*.xml".format(
            str(self.artifact_dir.relative_to("/")))
        self.teamcity_messages.importData("junit", junit_reports_pattern)

        # Instruct teamcity to upload our artifact directory
        artifact_path_pattern = "+:{}=>artifacts.tar.gz".format(
            str(self.artifact_dir.relative_to("/")))
        self.teamcity_messages.publishArtifacts(artifact_path_pattern)

    def run(self, args=None):
        args = args if args is not None else []

        # Let the user know what build is being run.
        # This makes it easier to retrieve the info from the logs.
        self.teamcity_messages.customMessage("Starting build {}".format(
            self.configuration.name),
                                             status="NORMAL")

        return_code, message = super().run()

        # Since we are aborting the build, make sure to flush everything first
        os.sync()

        if return_code != 0:
            # Add a build problem to the report
            self.teamcity_messages.buildProblem(
                message,
                # Let Teamcity calculate an ID from our message
                None)
            # Change the final build message
            self.teamcity_messages.buildStatus(
                # Don't change the status, let Teamcity set it to failure
                None,
                message)
        else:
            # Change the final build message but keep the original one as well
            self.teamcity_messages.buildStatus(
                # Don't change the status, let Teamcity set it to success
                None,
                "{} ({{build.status.text}})".format(message))

        return (return_code, message)