From ab4b6730a8894c108d595e09b2a997afb8404841 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Wed, 29 Jul 2020 17:00:10 -0300
Subject: [PATCH 01/26] Bump Conan version to 1.28.0

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/__init__.py      | 4 ++--
 cpt/requirements.txt | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/cpt/__init__.py b/cpt/__init__.py
index 8ea71dde..89f67b39 100644
--- a/cpt/__init__.py
+++ b/cpt/__init__.py
@@ -1,6 +1,6 @@
 
-__version__ = '0.34.0'
-NEWEST_CONAN_SUPPORTED = "1.27.000"
+__version__ = '0.34.1'
+NEWEST_CONAN_SUPPORTED = "1.28.000"
 
 
 def get_client_version():
diff --git a/cpt/requirements.txt b/cpt/requirements.txt
index 9838fc43..2620348c 100644
--- a/cpt/requirements.txt
+++ b/cpt/requirements.txt
@@ -1,3 +1,3 @@
 six>=1.10.0, <1.15.0
-conan>=1.7.0, <1.28.0
+conan>=1.7.0, <1.29.0
 tabulate>=0.8.0, <0.9.0

From 27614f74e9248095b268b90e6b12db4335b0e3a4 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Thu, 30 Jul 2020 10:39:18 -0300
Subject: [PATCH 02/26] #515 Fix CPT_TEST_FOLDER=False

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/runner.py                      |  3 +++
 cpt/test/integration/basic_test.py | 20 ++++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/cpt/runner.py b/cpt/runner.py
index d9315c4f..42769ccd 100644
--- a/cpt/runner.py
+++ b/cpt/runner.py
@@ -62,6 +62,9 @@ def __init__(self, profile_abs_path, reference, conan_api, uploader,
 
         self._profile = load_profile(profile_abs_path, cache)
 
+        if isinstance(self._test_folder, str) and self._test_folder.lower() == "false":
+            self._test_folder = False
+
     @property
     def settings(self):
         return self._profile.settings
diff --git a/cpt/test/integration/basic_test.py b/cpt/test/integration/basic_test.py
index 6dc3378a..7c4c02b9 100644
--- a/cpt/test/integration/basic_test.py
+++ b/cpt/test/integration/basic_test.py
@@ -310,6 +310,26 @@ def configure(self):
                 json_data = json.load(json_content)
                 self.assertFalse(json_data[0]["package"]["error"])
 
+    def test_disable_test_folder(self):
+        conanfile = """from conans import ConanFile
+
+class Pkg(ConanFile):
+    name = "lib"
+    version = "1.0"
+"""
+        self.save_conanfile(conanfile)
+        conanfile = """from conans import ConanFile
+
+class Pkg(ConanFile):
+    def test(self):
+        raise Exception("Should not run")
+"""
+        tools.save(os.path.join(self.tmp_folder, "test_package", "conanfile.py"), conanfile)
+        with tools.environment_append({"CPT_TEST_FOLDER": "False"}):
+            self.packager = ConanMultiPackager(out=self.output.write)
+            self.packager.add_common_builds()
+            self.packager.run()
+
     def test_custom_name_version(self):
         conanfile = """from conans import ConanFile
 from datetime import date

From 2d629177abb1dc1813d02ac4bd74295ef7507c81 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Thu, 30 Jul 2020 12:07:42 -0300
Subject: [PATCH 03/26] #515 Add invalid test_folder test

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 .travis.yml                        |  4 ++--
 cpt/test/integration/basic_test.py | 19 +++++++++++++++++++
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index e33b092a..03d6aa6e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,11 +16,11 @@ jobs:
     - stage: Conan Latest - Macos
       language: generic
       os: osx
-      osx_image: xcode8.3
+      osx_image: xcode12
       env: PYVER=py27 TOXENV=py27-conan-latest
     - language: generic
       os: osx
-      osx_image: xcode8.3
+      osx_image: xcode12
       env: PYVER=py37 TOXENV=py37-conan-latest
 
     - stage: Conan Development - Linux
diff --git a/cpt/test/integration/basic_test.py b/cpt/test/integration/basic_test.py
index 7c4c02b9..390757b6 100644
--- a/cpt/test/integration/basic_test.py
+++ b/cpt/test/integration/basic_test.py
@@ -5,6 +5,7 @@
 
 from conans import tools
 from conans.model.ref import ConanFileReference
+from conans.errors import ConanException
 
 from cpt.test.integration.base import BaseTest
 from cpt.packager import ConanMultiPackager
@@ -330,6 +331,24 @@ def test(self):
             self.packager.add_common_builds()
             self.packager.run()
 
+    def test_invalid_test_folder(self):
+        conanfile = """from conans import ConanFile
+
+class Pkg(ConanFile):
+    name = "lib"
+    version = "1.0"
+"""
+        self.save_conanfile(conanfile)
+        for test_folder in ["True", "foobar"]:
+            with tools.environment_append({"CPT_TEST_FOLDER": test_folder}):
+                self.packager = ConanMultiPackager(out=self.output.write)
+                self.packager.add_common_builds()
+                with self.assertRaises(ConanException) as raised:
+                    self.packager.run()
+                    self.assertIn("test folder '{}' not available, or it doesn't have a conanfile.py"
+                                  .format(test_folder),
+                                  str(raised.exception))
+
     def test_custom_name_version(self):
         conanfile = """from conans import ConanFile
 from datetime import date

From 584b56f9eb4c4e1ccc8307f874649a0cd7d9d7bb Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Thu, 30 Jul 2020 12:47:14 -0300
Subject: [PATCH 04/26] #515 Update OSX version to 10.3

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 .travis.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 03d6aa6e..55a7c3c5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,11 +16,11 @@ jobs:
     - stage: Conan Latest - Macos
       language: generic
       os: osx
-      osx_image: xcode12
+      osx_image: xcode10.3
       env: PYVER=py27 TOXENV=py27-conan-latest
     - language: generic
       os: osx
-      osx_image: xcode12
+      osx_image: xcode10.3
       env: PYVER=py37 TOXENV=py37-conan-latest
 
     - stage: Conan Development - Linux

From 8dc8d198ba52b19e3c759e92e7c552bd7b08c50a Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Tue, 1 Sep 2020 14:49:50 -0300
Subject: [PATCH 05/26] Support Conan 1.29

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/__init__.py      | 4 ++--
 cpt/requirements.txt | 2 +-
 setup.py             | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/cpt/__init__.py b/cpt/__init__.py
index 89f67b39..c8a4b0c5 100644
--- a/cpt/__init__.py
+++ b/cpt/__init__.py
@@ -1,6 +1,6 @@
 
-__version__ = '0.34.1'
-NEWEST_CONAN_SUPPORTED = "1.28.000"
+__version__ = '0.34.2'
+NEWEST_CONAN_SUPPORTED = "1.29.000"
 
 
 def get_client_version():
diff --git a/cpt/requirements.txt b/cpt/requirements.txt
index 2620348c..9594dac6 100644
--- a/cpt/requirements.txt
+++ b/cpt/requirements.txt
@@ -1,3 +1,3 @@
 six>=1.10.0, <1.15.0
-conan>=1.7.0, <1.29.0
+conan>=1.7.0, <1.30.0
 tabulate>=0.8.0, <0.9.0
diff --git a/setup.py b/setup.py
index 74ce48c9..44226547 100644
--- a/setup.py
+++ b/setup.py
@@ -47,8 +47,8 @@ def load_version():
     url='https://github.com/conan-io/conan-package-tools',
 
     # Author details
-    author='JFrog LTD. Luis Martinez de Bartolome',
-    author_email='luism@jfrog.com',
+    author='JFrog LTD',
+    author_email='info@conan.io',
 
     # Choose your license
     license='MIT',

From 6b06ed36a94a77999f63361e7db3d2ffeaaa41c3 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Thu, 1 Oct 2020 11:59:28 -0300
Subject: [PATCH 06/26] Accept Conan 1.30

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/__init__.py      | 4 ++--
 cpt/requirements.txt | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/cpt/__init__.py b/cpt/__init__.py
index c8a4b0c5..edceec8b 100644
--- a/cpt/__init__.py
+++ b/cpt/__init__.py
@@ -1,6 +1,6 @@
 
-__version__ = '0.34.2'
-NEWEST_CONAN_SUPPORTED = "1.29.000"
+__version__ = '0.34.3'
+NEWEST_CONAN_SUPPORTED = "1.30.000"
 
 
 def get_client_version():
diff --git a/cpt/requirements.txt b/cpt/requirements.txt
index 9594dac6..75f313ff 100644
--- a/cpt/requirements.txt
+++ b/cpt/requirements.txt
@@ -1,3 +1,3 @@
 six>=1.10.0, <1.15.0
-conan>=1.7.0, <1.30.0
+conan>=1.7.0, <1.31.0
 tabulate>=0.8.0, <0.9.0

From 4fc377e55ba871005f7ded9439fca33830af3345 Mon Sep 17 00:00:00 2001
From: ericLemanissier <ericLemanissier@users.noreply.github.com>
Date: Tue, 25 Feb 2020 12:24:43 +0100
Subject: [PATCH 07/26] build static before shared

usually, the static build is the default one
---
 cpt/builds_generator.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/cpt/builds_generator.py b/cpt/builds_generator.py
index 4eba274c..6781a471 100644
--- a/cpt/builds_generator.py
+++ b/cpt/builds_generator.py
@@ -221,7 +221,7 @@ def get_mingw_builds(mingw_configurations, mingw_installer_reference,
         build_requires = {"*": [mingw_installer_reference]}
 
         if shared_option_name and not build_all_options_values:
-            for shared in [True, False]:
+            for shared in [False, True]:
                 opt = copy.copy(options)
                 opt[shared_option_name] = shared
                 builds += _make_mingw_builds(settings, opt, build_requires, build_types, cppstds, reference)
@@ -367,7 +367,7 @@ def get_osx_apple_clang_builds(apple_clang_versions, archs, shared_option_name,
         for arch in archs:
             for cppstd in cppstds:
                 if shared_option_name and not build_all_options_values:
-                    for shared in [True, False]:
+                    for shared in [False, True]:
                         opt = copy.copy(options)
                         opt[shared_option_name] = shared
                         for build_type_it in build_types:
@@ -414,7 +414,7 @@ def get_linux_gcc_builds(gcc_versions, archs, shared_option_name, pure_c, build_
         for arch in archs:
             for cppstd in cppstds:
                 if shared_option_name and not build_all_options_values:
-                    for shared in [True, False]:
+                    for shared in [False, True]:
                         opt = copy.copy(options)
                         opt[shared_option_name] = shared
                         for build_type_it in build_types:
@@ -464,7 +464,7 @@ def get_linux_clang_builds(clang_versions, archs, shared_option_name, pure_c, bu
         for arch in archs:
             for cppstd in cppstds:
                 if shared_option_name and not build_all_options_values:
-                    for shared in [True, False]:
+                    for shared in [False, True]:
                         opt = copy.copy(options)
                         opt[shared_option_name] = shared
                         for build_type_it in build_types:

From ed5b0218dec6f9ea8b844ae6931bec5fd8f5d1db Mon Sep 17 00:00:00 2001
From: Eric Lemanissier <eric.lemanissier@gmail.com>
Date: Tue, 27 Oct 2020 22:28:31 +0100
Subject: [PATCH 08/26] fix unit tests

---
 cpt/test/unit/generators_test.py | 178 +++++++++++++++----------------
 cpt/test/unit/packager_test.py   |   8 +-
 2 files changed, 93 insertions(+), 93 deletions(-)

diff --git a/cpt/test/unit/generators_test.py b/cpt/test/unit/generators_test.py
index eeb3403f..7510cd64 100644
--- a/cpt/test/unit/generators_test.py
+++ b/cpt/test/unit/generators_test.py
@@ -20,25 +20,25 @@ def test_mingw_generator(self):
             ({'build_type': 'Release', 'compiler.version': '4.9', 'compiler.libcxx': "libstdc++",
               'compiler': 'gcc', 'arch': 'x86', 'compiler.exception': 'dwarf2',
               'compiler.threads': 'posix'},
-             {'pack:shared': True},
+             {'pack:shared': False},
              {},
              {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, ref),
             ({'compiler.version': '4.9', 'compiler': 'gcc', 'compiler.libcxx': "libstdc++",
               'build_type': 'Debug', 'compiler.exception': 'dwarf2', 'compiler.threads': 'posix',
-              'arch': 'x86'},             {'pack:shared': True},
+              'arch': 'x86'},             {'pack:shared': False},
              {},
              {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, ref),
 
             ({'build_type': 'Release', 'compiler.version': '4.9', 'compiler.libcxx': "libstdc++",
               'compiler': 'gcc', 'arch': 'x86', 'compiler.exception': 'dwarf2',
               'compiler.threads': 'posix'},
-             {'pack:shared': False},
+             {'pack:shared': True},
                 {},
                 {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, ref),
             ({'compiler.version': '4.9', 'compiler': 'gcc', 'compiler.libcxx': "libstdc++",
               'build_type': 'Debug', 'compiler.exception': 'dwarf2', 'compiler.threads': 'posix',
               'arch': 'x86'},
-             {'pack:shared': False},
+             {'pack:shared': True},
              {},
              {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, ref)]
 
@@ -50,13 +50,13 @@ def test_mingw_generator(self):
             ({'build_type': 'Release', 'compiler.version': '4.9', 'compiler.libcxx': "libstdc++",
               'compiler': 'gcc', 'arch': 'x86', 'compiler.exception': 'dwarf2',
               'compiler.threads': 'posix', 'compiler.cppstd': '20'},
-             {'pack:shared': True},
+             {'pack:shared': False},
              {},
              {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, None),
             ({'build_type': 'Release', 'compiler.version': '4.9', 'compiler.libcxx': "libstdc++",
               'compiler': 'gcc', 'arch': 'x86', 'compiler.exception': 'dwarf2',
               'compiler.threads': 'posix', 'compiler.cppstd': '20'},
-             {'pack:shared': False},
+             {'pack:shared': True},
              {},
              {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, None)]
 
@@ -68,13 +68,13 @@ def test_mingw_generator(self):
             ({'compiler.version': '4.9', 'compiler': 'gcc', 'compiler.libcxx': "libstdc++",
               'build_type': 'Debug', 'compiler.exception': 'dwarf2', 'compiler.threads': 'posix',
               'arch': 'x86', 'compiler.cppstd': '14'},
-             {'pack:shared': True},
+             {'pack:shared': False},
              {},
              {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, None),
             ({'compiler.version': '4.9', 'compiler': 'gcc', 'compiler.libcxx': "libstdc++",
               'build_type': 'Debug', 'compiler.exception': 'dwarf2', 'compiler.threads': 'posix',
               'arch': 'x86', 'compiler.cppstd': '14'},
-             {'pack:shared': False},
+             {'pack:shared': True},
              {},
              {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, None)]
 
@@ -89,26 +89,26 @@ def test_mingw_generator(self):
             ({'build_type': 'Release', 'compiler.version': '4.9', 'compiler.libcxx': "libstdc++",
               'compiler': 'gcc', 'arch': 'x86', 'compiler.exception': 'dwarf2',
               'compiler.threads': 'posix'},
-             {'pack:shared': True, "pack:foobar": True, "foobar:qux": "data"},
+             {'pack:shared': False, "pack:foobar": True, "foobar:qux": "data"},
              {},
              {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, ref),
             ({'compiler.version': '4.9', 'compiler': 'gcc', 'compiler.libcxx': "libstdc++",
               'build_type': 'Debug', 'compiler.exception': 'dwarf2', 'compiler.threads': 'posix',
               'arch': 'x86'},
-             {'pack:shared': True, "pack:foobar": True, "foobar:qux": "data"},
+             {'pack:shared': False, "pack:foobar": True, "foobar:qux": "data"},
              {},
              {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, ref),
 
             ({'build_type': 'Release', 'compiler.version': '4.9', 'compiler.libcxx': "libstdc++",
               'compiler': 'gcc', 'arch': 'x86', 'compiler.exception': 'dwarf2',
               'compiler.threads': 'posix'},
-             {'pack:shared': False, "pack:foobar": True, "foobar:qux": "data"},
+             {'pack:shared': True, "pack:foobar": True, "foobar:qux": "data"},
              {},
              {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, ref),
             ({'compiler.version': '4.9', 'compiler': 'gcc', 'compiler.libcxx': "libstdc++",
               'build_type': 'Debug', 'compiler.exception': 'dwarf2', 'compiler.threads': 'posix',
               'arch': 'x86'},
-             {'pack:shared': False, "pack:foobar": True, "foobar:qux": "data"},
+             {'pack:shared': True, "pack:foobar": True, "foobar:qux": "data"},
              {},
              {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}, ref)]
 
@@ -255,13 +255,13 @@ def test_get_osx_apple_clang_builds(self):
                                             options={},
                                             reference=ref)
         expected = [({'arch': 'x86_64', 'compiler.libcxx': 'libc++', 'compiler': 'apple-clang', 'compiler.version': '8.0', 'build_type': 'Debug'},
-                     {'pack:shared': True}, {}, {}, ref),
+                     {'pack:shared': False}, {}, {}, ref),
                     ({'arch': 'x86_64', 'compiler.libcxx': 'libc++', 'compiler': 'apple-clang', 'compiler.version': '8.0', 'build_type': 'Release'},
-                     {'pack:shared': True}, {}, {}, ref),
-                    ({'arch': 'x86_64', 'compiler.libcxx': 'libc++', 'compiler': 'apple-clang', 'compiler.version': '8.0', 'build_type': 'Debug'},
                      {'pack:shared': False}, {}, {}, ref),
+                    ({'arch': 'x86_64', 'compiler.libcxx': 'libc++', 'compiler': 'apple-clang', 'compiler.version': '8.0', 'build_type': 'Debug'},
+                     {'pack:shared': True}, {}, {}, ref),
                     ({'arch': 'x86_64', 'compiler.libcxx': 'libc++', 'compiler': 'apple-clang', 'compiler.version': '8.0', 'build_type': 'Release'},
-                     {'pack:shared': False}, {}, {}, ref)]
+                     {'pack:shared': True}, {}, {}, ref)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_osx_apple_clang_builds(["8.0"], ["x86_64"], "pack:shared", pure_c=True,
@@ -270,50 +270,50 @@ def test_get_osx_apple_clang_builds(self):
                                             options={})
         expected = [({'arch': 'x86_64', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Debug'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'arch': 'x86_64', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Release'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'arch': 'x86_64', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Debug'},
-                     {'pack:shared': False}, {}, {}, None),
+                     {'pack:shared': True}, {}, {}, None),
                     ({'arch': 'x86_64', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Release'},
-                     {'pack:shared': False}, {}, {}, None)]
+                     {'pack:shared': True}, {}, {}, None)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_osx_apple_clang_builds(["8.0"], ["x86_64"], "pack:shared", pure_c=False, build_types=["Debug"], cppstds=["14"], options={})
         expected = [({'arch': 'x86_64', 'compiler.libcxx': 'libc++', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Debug', 'compiler.cppstd': '14'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'arch': 'x86_64', 'compiler.libcxx': 'libc++', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Debug', 'compiler.cppstd': '14'},
-                     {'pack:shared': False}, {}, {}, None)]
+                     {'pack:shared': True}, {}, {}, None)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_osx_apple_clang_builds(["8.0"], ["x86_64"], "pack:shared", pure_c=True, build_types=["Release"], cppstds=["17"], options={})
         expected = [({'arch': 'x86_64', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Release'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'arch': 'x86_64', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Release'},
-                     {'pack:shared': False}, {}, {}, None)]
+                     {'pack:shared': True}, {}, {}, None)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_osx_apple_clang_builds(["8.0"], ["x86_64"], "pack:shared", pure_c=False,
                                             build_types=["Debug", "Release"], cppstds=[None], options={"qux:foobar": False, "foo:pkg": "bar"}, reference=ref)
         expected = [({'arch': 'x86_64', 'compiler.libcxx': 'libc++', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Debug'},
-                     {'pack:shared': True, "qux:foobar": False, "foo:pkg": "bar"}, {}, {}, ref),
+                     {'pack:shared': False, "qux:foobar": False, "foo:pkg": "bar"}, {}, {}, ref),
                     ({'arch': 'x86_64', 'compiler.libcxx': 'libc++', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Release'},
-                     {'pack:shared': True, "qux:foobar": False, "foo:pkg": "bar"}, {}, {}, ref),
+                     {'pack:shared': False, "qux:foobar": False, "foo:pkg": "bar"}, {}, {}, ref),
                     ({'arch': 'x86_64', 'compiler.libcxx': 'libc++', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Debug'},
-                     {'pack:shared': False, "qux:foobar": False, "foo:pkg": "bar"}, {}, {}, ref),
+                     {'pack:shared': True, "qux:foobar": False, "foo:pkg": "bar"}, {}, {}, ref),
                     ({'arch': 'x86_64', 'compiler.libcxx': 'libc++', 'compiler': 'apple-clang',
                       'compiler.version': '8.0', 'build_type': 'Release'},
-                     {'pack:shared': False, "qux:foobar": False, "foo:pkg": "bar"}, {}, {}, ref)]
+                     {'pack:shared': True, "qux:foobar": False, "foo:pkg": "bar"}, {}, {}, ref)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_osx_apple_clang_builds(["8.0"], ["x86_64"], None, pure_c=False,
@@ -397,104 +397,104 @@ def test_get_osx_apple_clang_builds(self):
     def test_get_linux_gcc_builds(self):
         builds = get_linux_gcc_builds(["6"], ["x86_64"], "pack:shared", pure_c=False, build_types=["Debug", "Release"], cppstds=[None], options={})
         expected = [({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++', 'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++11', 'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++', 'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++11', 'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': True}, {}, {}, None),
-                    ({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++', 'compiler.version': '6', 'arch': 'x86_64'},
                      {'pack:shared': False}, {}, {}, None),
+                    ({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++', 'compiler.version': '6', 'arch': 'x86_64'},
+                     {'pack:shared': True}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++11', 'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': False}, {}, {}, None),
+                     {'pack:shared': True}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++', 'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': False}, {}, {}, None),
+                     {'pack:shared': True}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++11', 'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': False}, {}, {}, None)]
+                     {'pack:shared': True}, {}, {}, None)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_gcc_builds(["6"], ["x86_64"], "pack:shared", pure_c=True, build_types=["Debug", "Release"], cppstds=[None], options={})
         expected = [({'arch': 'x86_64', 'compiler.version': '6', 'build_type': 'Debug', 'compiler': 'gcc'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'arch': 'x86_64', 'compiler.version': '6', 'build_type': 'Release', 'compiler': 'gcc'},
-                     {'pack:shared': True}, {}, {}, None),
-                    ({'arch': 'x86_64', 'compiler.version': '6', 'build_type': 'Debug', 'compiler': 'gcc'},
                      {'pack:shared': False}, {}, {}, None),
+                    ({'arch': 'x86_64', 'compiler.version': '6', 'build_type': 'Debug', 'compiler': 'gcc'},
+                     {'pack:shared': True}, {}, {}, None),
                     ({'arch': 'x86_64', 'compiler.version': '6', 'build_type': 'Release', 'compiler': 'gcc'},
-                     {'pack:shared': False}, {}, {}, None)]
+                     {'pack:shared': True}, {}, {}, None)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_gcc_builds(["6"], ["x86_64"], "pack:shared", pure_c=False, build_types=["Debug"], cppstds=["14"], options={})
         expected = [({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '6', 'arch': 'x86_64', 'compiler.cppstd': '14'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++11',
                       'compiler.version': '6', 'arch': 'x86_64', 'compiler.cppstd': '14'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '6', 'arch': 'x86_64', 'compiler.cppstd': '14'},
-                     {'pack:shared': False}, {}, {}, None),
+                     {'pack:shared': True}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++11',
                       'compiler.version': '6', 'arch': 'x86_64', 'compiler.cppstd': '14'},
-                     {'pack:shared': False}, {}, {}, None)]
+                     {'pack:shared': True}, {}, {}, None)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_gcc_builds(["6"], ["x86_64"], "pack:shared", pure_c=True, build_types=["Debug"], cppstds=["14"], options={})
         expected = [({'arch': 'x86_64', 'compiler.version': '6', 'build_type': 'Debug', 'compiler': 'gcc'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'arch': 'x86_64', 'compiler.version': '6', 'build_type': 'Debug', 'compiler': 'gcc'},
-                     {'pack:shared': False}, {}, {}, None)]
+                     {'pack:shared': True}, {}, {}, None)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_gcc_builds(["6"], ["x86_64"], "pack:shared", pure_c=False, build_types=["Release"], cppstds=["17"], options={})
         expected = [({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '6', 'arch': 'x86_64', 'compiler.cppstd': '17'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++11',
                       'compiler.version': '6', 'arch': 'x86_64', 'compiler.cppstd': '17'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '6', 'arch': 'x86_64', 'compiler.cppstd': '17'},
-                     {'pack:shared': False}, {}, {}, None),
+                     {'pack:shared': True}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++11',
                       'compiler.version': '6', 'arch': 'x86_64', 'compiler.cppstd': '17'},
-                     {'pack:shared': False}, {}, {}, None)]
+                     {'pack:shared': True}, {}, {}, None)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_gcc_builds(["6"], ["x86_64"], "pack:shared", pure_c=True, build_types=["Release"], cppstds=["17"], options={})
         expected = [({'arch': 'x86_64', 'compiler.version': '6', 'build_type': 'Release', 'compiler': 'gcc'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'arch': 'x86_64', 'compiler.version': '6', 'build_type': 'Release', 'compiler': 'gcc'},
-                     {'pack:shared': False}, {}, {}, None)]
+                     {'pack:shared': True}, {}, {}, None)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_gcc_builds(["6"], ["x86_64"], "pack:shared", pure_c=False, build_types=["Debug", "Release"],
                                       cppstds=[None], options={"foo:bar": "qux", "pkg:qux": False})
         expected = [({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++', 'compiler.version': '6',
                       'arch': 'x86_64'},
-                     {'pack:shared': True, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
+                     {'pack:shared': False, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++11',
                       'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': True, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
+                     {'pack:shared': False, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': True, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
+                     {'pack:shared': False, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++11',
                       'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': True, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
+                     {'pack:shared': False, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++', 'compiler.version': '6',
                       'arch': 'x86_64'},
-                     {'pack:shared': False, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
+                     {'pack:shared': True, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++11',
                       'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': False, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
+                     {'pack:shared': True, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': False, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
+                     {'pack:shared': True, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None),
                     ({'compiler': 'gcc', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++11',
                       'compiler.version': '6', 'arch': 'x86_64'},
-                     {'pack:shared': False, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None)]
+                     {'pack:shared': True, "foo:bar": "qux", "pkg:qux": False}, {}, {}, None)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_gcc_builds(["6"], ["x86_64"], None, pure_c=True, build_types=["Debug", "Release"],
@@ -627,84 +627,84 @@ def test_get_linux_clang_builds(self):
                                         build_types=["Debug", "Release"], cppstds=[None], options={},
                                         reference=ref)
         expected = [({'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++', 'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': True}, {}, {}, ref),
+                     {'pack:shared': False}, {}, {}, ref),
                     ({'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libc++', 'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': True}, {}, {}, ref),
+                     {'pack:shared': False}, {}, {}, ref),
                     ({'compiler': 'clang', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++', 'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': True}, {}, {}, ref),
+                     {'pack:shared': False}, {}, {}, ref),
                     ({'compiler': 'clang', 'build_type': 'Release', 'compiler.libcxx': 'libc++', 'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': True}, {}, {}, ref),
-                    ({'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++', 'compiler.version': '4.0', 'arch': 'x86_64'},
                      {'pack:shared': False}, {}, {}, ref),
+                    ({'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++', 'compiler.version': '4.0', 'arch': 'x86_64'},
+                     {'pack:shared': True}, {}, {}, ref),
                     ({'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libc++', 'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': False}, {}, {}, ref),
+                     {'pack:shared': True}, {}, {}, ref),
                     ({'compiler': 'clang', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++', 'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': False}, {}, {}, ref),
+                     {'pack:shared': True}, {}, {}, ref),
                     ({'compiler': 'clang', 'build_type': 'Release', 'compiler.libcxx': 'libc++', 'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': False}, {}, {}, ref)]
+                     {'pack:shared': True}, {}, {}, ref)]
         b = [tuple(a) for a in builds]
         self.assertEquals(b, expected)
 
         builds = get_linux_clang_builds(["4.0"], ["x86_64"], "pack:shared", pure_c=True,
                                         build_types=["Debug", "Release"], cppstds=[None], options={}, reference=ref)
         expected = [({'arch': 'x86_64', 'compiler.version': '4.0', 'build_type': 'Debug', 'compiler': 'clang'},
-                     {'pack:shared': True}, {}, {}, ref),
+                     {'pack:shared': False}, {}, {}, ref),
                     ({'arch': 'x86_64', 'compiler.version': '4.0', 'build_type': 'Release', 'compiler': 'clang'},
-                     {'pack:shared': True}, {}, {}, ref),
-                    ({'arch': 'x86_64', 'compiler.version': '4.0', 'build_type': 'Debug', 'compiler': 'clang'},
                      {'pack:shared': False}, {}, {}, ref),
+                    ({'arch': 'x86_64', 'compiler.version': '4.0', 'build_type': 'Debug', 'compiler': 'clang'},
+                     {'pack:shared': True}, {}, {}, ref),
                     ({'arch': 'x86_64', 'compiler.version': '4.0', 'build_type': 'Release', 'compiler': 'clang'},
-                     {'pack:shared': False}, {}, {}, ref)]
+                     {'pack:shared': True}, {}, {}, ref)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_clang_builds(["4.0"], ["x86_64"], "pack:shared", pure_c=False,
                                         build_types=["Debug"], cppstds=[None], options={}, reference=ref)
         expected = [({'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': True}, {}, {}, ref),
+                     {'pack:shared': False}, {}, {}, ref),
                     ({'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libc++', 'compiler.version': '4.0',
                      'arch': 'x86_64'},
-                    {'pack:shared': True}, {}, {}, ref),
+                    {'pack:shared': False}, {}, {}, ref),
                     ({'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': False}, {}, {}, ref),
+                     {'pack:shared': True}, {}, {}, ref),
                     ({'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libc++', 'compiler.version': '4.0',
                      'arch': 'x86_64'},
-                    {'pack:shared': False}, {}, {}, ref)]
+                    {'pack:shared': True}, {}, {}, ref)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_clang_builds(["4.0"], ["x86_64"], "pack:shared", pure_c=True,
                                         build_types=["Debug"], cppstds=[None], options={}, reference=ref)
         expected = [({'arch': 'x86_64', 'compiler.version': '4.0', 'build_type': 'Debug', 'compiler': 'clang'},
-                     {'pack:shared': True}, {}, {}, ref),
+                     {'pack:shared': False}, {}, {}, ref),
                     ({'arch': 'x86_64', 'compiler.version': '4.0', 'build_type': 'Debug', 'compiler': 'clang'},
-                     {'pack:shared': False}, {}, {}, ref)]
+                     {'pack:shared': True}, {}, {}, ref)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_clang_builds(["4.0"], ["x86_64"], "pack:shared", pure_c=False,
                                         build_types=["Release"], cppstds=[None], options={}, reference=ref)
         expected = [({'compiler': 'clang', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': True}, {}, {}, ref),
+                     {'pack:shared': False}, {}, {}, ref),
                     (
                     {'compiler': 'clang', 'build_type': 'Release', 'compiler.libcxx': 'libc++', 'compiler.version': '4.0',
                      'arch': 'x86_64'},
-                    {'pack:shared': True}, {}, {}, ref),
+                    {'pack:shared': False}, {}, {}, ref),
                     ({'compiler': 'clang', 'build_type': 'Release', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': False}, {}, {}, ref),
+                     {'pack:shared': True}, {}, {}, ref),
                     (
                     {'compiler': 'clang', 'build_type': 'Release', 'compiler.libcxx': 'libc++', 'compiler.version': '4.0',
                      'arch': 'x86_64'},
-                    {'pack:shared': False}, {}, {}, ref)]
+                    {'pack:shared': True}, {}, {}, ref)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_clang_builds(["4.0"], ["x86_64"], "pack:shared", pure_c=True, build_types=["Release"],
                                         cppstds=[None], options={}, reference=None)
         expected = [({'arch': 'x86_64', 'compiler.version': '4.0', 'build_type': 'Release', 'compiler': 'clang'},
-                     {'pack:shared': True}, {}, {}, None),
+                     {'pack:shared': False}, {}, {}, None),
                     ({'arch': 'x86_64', 'compiler.version': '4.0', 'build_type': 'Release', 'compiler': 'clang'},
-                     {'pack:shared': False}, {}, {}, None)]
+                     {'pack:shared': True}, {}, {}, None)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_clang_builds(["4.0"], ["x86_64"], "pack:shared", pure_c=False,
@@ -712,18 +712,18 @@ def test_get_linux_clang_builds(self):
                                         options={"foo:bar": "qux", "pkg:shared": True}, reference=ref)
         expected = [({'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': True, "foo:bar": "qux", "pkg:shared": True}, {}, {}, ref),
+                     {'pack:shared': False, "foo:bar": "qux", "pkg:shared": True}, {}, {}, ref),
                     (
                     {'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libc++', 'compiler.version': '4.0',
                      'arch': 'x86_64'},
-                    {'pack:shared': True, "foo:bar": "qux", "pkg:shared": True}, {}, {}, ref),
+                    {'pack:shared': False, "foo:bar": "qux", "pkg:shared": True}, {}, {}, ref),
                     ({'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libstdc++',
                       'compiler.version': '4.0', 'arch': 'x86_64'},
-                     {'pack:shared': False, "foo:bar": "qux", "pkg:shared": True}, {}, {}, ref),
+                     {'pack:shared': True, "foo:bar": "qux", "pkg:shared": True}, {}, {}, ref),
                     (
                     {'compiler': 'clang', 'build_type': 'Debug', 'compiler.libcxx': 'libc++', 'compiler.version': '4.0',
                      'arch': 'x86_64'},
-                    {'pack:shared': False, "foo:bar": "qux", "pkg:shared": True}, {}, {}, ref)]
+                    {'pack:shared': True, "foo:bar": "qux", "pkg:shared": True}, {}, {}, ref)]
         self.assertEquals([tuple(a) for a in builds], expected)
 
         builds = get_linux_clang_builds(["4.0"], ["x86_64"], None, pure_c=False,
@@ -1448,7 +1448,7 @@ def test_visual_toolsets(self):
                     {'pack:shared': True, 'pack:foo': False, 'pack:bar': False}, {}, {}, ref), (
                     {'compiler': 'Visual Studio', 'compiler.version': '10', 'arch': 'x86',
                      'build_type': 'Debug', 'compiler.runtime': 'MDd'},
-                    {'pack:shared': False, 'pack:foo': True, 'pack:bar': True}, {}, {}, ref), (
+                     {'pack:shared': False, 'pack:foo': True, 'pack:bar': True}, {}, {}, ref), (
                     {'compiler': 'Visual Studio', 'compiler.version': '10', 'arch': 'x86',
                      'build_type': 'Debug', 'compiler.runtime': 'MDd'},
                     {'pack:shared': False, 'pack:foo': False, 'pack:bar': True}, {}, {}, ref), (
diff --git a/cpt/test/unit/packager_test.py b/cpt/test/unit/packager_test.py
index 9816b351..eceb64fa 100644
--- a/cpt/test/unit/packager_test.py
+++ b/cpt/test/unit/packager_test.py
@@ -430,26 +430,26 @@ def test_only_mingw(self):
         expected = [({'compiler.exception': 'seh', 'compiler.libcxx': "libstdc++",
                       'compiler.threads': 'posix', 'compiler.version': '4.9', 'arch': 'x86_64',
                       'build_type': 'Release', 'compiler': 'gcc'},
-                     {'zlib:shared': True},
+                     {'zlib:shared': False},
                      {},
                      {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}),
                     ({'compiler.exception': 'seh', 'compiler.libcxx': "libstdc++", 'arch': 'x86_64',
                       'compiler.threads': 'posix', 'compiler.version': '4.9', 'build_type': 'Debug',
                       'compiler': 'gcc'},
-                     {'zlib:shared': True},
+                     {'zlib:shared': False},
                      {},
                      {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}),
 
                     ({'compiler.exception': 'seh', 'compiler.libcxx': "libstdc++",
                       'compiler.threads': 'posix', 'compiler.version': '4.9', 'arch': 'x86_64',
                       'build_type': 'Release', 'compiler': 'gcc'},
-                     {'zlib:shared': False},
+                     {'zlib:shared': True},
                      {},
                      {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]}),
                     ({'compiler.exception': 'seh', 'compiler.libcxx': "libstdc++", 'arch': 'x86_64',
                       'compiler.threads': 'posix', 'compiler.version': '4.9', 'build_type': 'Debug',
                       'compiler': 'gcc'},
-                     {'zlib:shared': False},
+                     {'zlib:shared': True},
                      {},
                      {'*': [ConanFileReference.loads("mingw_installer/1.0@conan/stable")]})]
 

From 245b72193b95a8d3d2fb8e70b53fbb37d11cd2aa Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Fri, 30 Oct 2020 09:23:23 -0300
Subject: [PATCH 09/26] Update max Conan version to 1.31.0

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/__init__.py      | 4 ++--
 cpt/requirements.txt | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/cpt/__init__.py b/cpt/__init__.py
index edceec8b..89d9d5aa 100644
--- a/cpt/__init__.py
+++ b/cpt/__init__.py
@@ -1,6 +1,6 @@
 
-__version__ = '0.34.3'
-NEWEST_CONAN_SUPPORTED = "1.30.000"
+__version__ = '0.34.4'
+NEWEST_CONAN_SUPPORTED = "1.31.000"
 
 
 def get_client_version():
diff --git a/cpt/requirements.txt b/cpt/requirements.txt
index 75f313ff..80f2dbf4 100644
--- a/cpt/requirements.txt
+++ b/cpt/requirements.txt
@@ -1,3 +1,3 @@
-six>=1.10.0, <1.15.0
-conan>=1.7.0, <1.31.0
+six>=1.10.0,<=1.15.0
+conan>=1.7.0, <1.32.0
 tabulate>=0.8.0, <0.9.0

From c0a316530f21b5d7137a31a79c784e48625dafe5 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Tue, 17 Nov 2020 12:59:59 -0300
Subject: [PATCH 10/26] Bump dev version (#531)

* Bump dev version

Signed-off-by: Uilian Ries <uilianries@gmail.com>

* Allow dev branch

Signed-off-by: Uilian Ries <uilianries@gmail.com>

* Remove maximum Conan version

Signed-off-by: Uilian Ries <uilianries@gmail.com>

* Remove conan version

Signed-off-by: Uilian Ries <uilianries@gmail.com>

* Copy Conan Tests to CPT

Signed-off-by: Uilian Ries <uilianries@gmail.com>

* Remove unused files

Signed-off-by: Uilian Ries <uilianries@gmail.com>

* Export source for fPIC template

Signed-off-by: Uilian Ries <uilianries@gmail.com>

* Fix integration test according Conan version

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 .gitignore                                    |   8 +
 cpt/__init__.py                               |   4 +-
 cpt/packager.py                               |  16 +-
 cpt/requirements.txt                          |   2 +-
 cpt/test/assets/__init__.py                   |  16 +
 cpt/test/assets/genconanfile.py               | 390 ++++++++
 cpt/test/integration/base.py                  |  10 +-
 cpt/test/integration/conan_version_test.py    |  18 -
 cpt/test/integration/update_deps_test.py      |   2 +-
 .../integration/update_python_reqs_test.py    |   2 +-
 cpt/test/integration/update_some_deps_test.py |   2 +-
 cpt/test/integration/upload_test.py           |   2 +-
 cpt/test/test_client/config_install_test.py   |   2 +-
 .../test_client/invalid_config_checks_test.py |   2 +-
 cpt/test/test_client/upload_checks_test.py    |   2 +-
 cpt/test/test_client/visual_toolsets_test.py  |   2 +-
 cpt/test/unit/auth_test.py                    |   2 +-
 cpt/test/unit/ci_manager_test.py              |   2 +-
 cpt/test/unit/packager_test.py                |   2 +-
 cpt/test/unit/utils.py                        |   2 +-
 cpt/test/utils/__init__.py                    |   0
 cpt/test/utils/mocks.py                       | 231 +++++
 cpt/test/utils/scm.py                         | 126 +++
 cpt/test/utils/server_launcher.py             | 113 +++
 cpt/test/utils/test_files.py                  |  91 ++
 cpt/test/utils/tools.py                       | 898 ++++++++++++++++++
 26 files changed, 1895 insertions(+), 52 deletions(-)
 create mode 100644 cpt/test/assets/__init__.py
 create mode 100644 cpt/test/assets/genconanfile.py
 delete mode 100644 cpt/test/integration/conan_version_test.py
 create mode 100644 cpt/test/utils/__init__.py
 create mode 100644 cpt/test/utils/mocks.py
 create mode 100644 cpt/test/utils/scm.py
 create mode 100644 cpt/test/utils/server_launcher.py
 create mode 100644 cpt/test/utils/test_files.py
 create mode 100644 cpt/test/utils/tools.py

diff --git a/.gitignore b/.gitignore
index d5ed69ad..aa2f6a10 100644
--- a/.gitignore
+++ b/.gitignore
@@ -97,3 +97,11 @@ cacert.pem
 
 # tmp eclipse markdown viewer
 /.README*
+
+
+# Virtualenv
+env/
+venv/
+
+# Coverage
+.coveragerc
diff --git a/cpt/__init__.py b/cpt/__init__.py
index 89d9d5aa..eccc02cf 100644
--- a/cpt/__init__.py
+++ b/cpt/__init__.py
@@ -1,10 +1,10 @@
 
-__version__ = '0.34.4'
-NEWEST_CONAN_SUPPORTED = "1.31.000"
+__version__ = '0.34.5-dev'
 
 
 def get_client_version():
     from conans.model.version import Version
     from conans import __version__ as client_version
+    from os import getenv
     # It is a mess comparing dev versions, lets assume that the -dev is the further release
     return Version(client_version.replace("-dev", ""))
diff --git a/cpt/packager.py b/cpt/packager.py
index bd0166f7..7f310715 100644
--- a/cpt/packager.py
+++ b/cpt/packager.py
@@ -13,7 +13,7 @@
 from conans.model.ref import ConanFileReference
 from conans.model.version import Version
 
-from cpt import NEWEST_CONAN_SUPPORTED, get_client_version
+from cpt import get_client_version
 from cpt.auth import AuthManager
 from cpt.builds_generator import BuildConf, BuildGenerator
 from cpt.ci_manager import CIManager
@@ -346,18 +346,6 @@ def valid_pair(var, value):
                                      for var, value in self.__dict__.items()
                                      if valid_pair(var, value)})
 
-        self._newest_supported_conan_version = Version(NEWEST_CONAN_SUPPORTED).minor(fill=False)
-        self._client_conan_version = conan_version
-
-    def _check_conan_version(self):
-        tmp = self._newest_supported_conan_version
-        if Version(self._client_conan_version).minor(fill=False) > tmp:
-            msg = "Conan/CPT version mismatch. Conan version installed: " \
-                  "%s . This version of CPT supports only Conan < %s" \
-                  "" % (self._client_conan_version, str(tmp))
-            self.printer.print_message(msg)
-            raise Exception(msg)
-
     # For Docker on Windows, including Linux containers on Windows
     @property
     def is_lcow(self):
@@ -569,8 +557,6 @@ def update_build_if(self, predicate, new_settings=None, new_options=None, new_en
         self._builds = updated_builds
 
     def run(self, base_profile_name=None, summary_file=None):
-        self._check_conan_version()
-
         env_vars = self.auth_manager.env_vars()
         env_vars.update(self.remotes_manager.env_vars())
         with tools.environment_append(env_vars):
diff --git a/cpt/requirements.txt b/cpt/requirements.txt
index 80f2dbf4..981153dc 100644
--- a/cpt/requirements.txt
+++ b/cpt/requirements.txt
@@ -1,3 +1,3 @@
 six>=1.10.0,<=1.15.0
-conan>=1.7.0, <1.32.0
+conan>=1.7.0
 tabulate>=0.8.0, <0.9.0
diff --git a/cpt/test/assets/__init__.py b/cpt/test/assets/__init__.py
new file mode 100644
index 00000000..c7f40f86
--- /dev/null
+++ b/cpt/test/assets/__init__.py
@@ -0,0 +1,16 @@
+import os
+import shutil
+
+
+def copy_assets(src_folder, dst_folder, assets=None):
+    assets_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "files")
+    if src_folder:
+        src_folder = os.path.join(assets_path, src_folder)
+    assets = assets or os.listdir(src_folder)
+    for asset in assets:
+        s = os.path.join(src_folder, asset)
+        d = os.path.join(dst_folder, asset)
+        if os.path.isdir(s):
+            shutil.copytree(s, d)
+        else:
+            shutil.copy2(s, d)
diff --git a/cpt/test/assets/genconanfile.py b/cpt/test/assets/genconanfile.py
new file mode 100644
index 00000000..f253d584
--- /dev/null
+++ b/cpt/test/assets/genconanfile.py
@@ -0,0 +1,390 @@
+from conans.model.ref import ConanFileReference
+
+
+class GenConanfile(object):
+    """
+    USAGE:
+
+    x = GenConanfile().with_import("import os").\
+        with_setting("os").\
+        with_option("shared", [True, False]).\
+        with_default_option("shared", True).\
+        with_build_msg("holaaa").\
+        with_build_msg("adiooos").\
+        with_package_file("file.txt", "hola").\
+        with_package_file("file2.txt", "hola")
+    """
+
+    def __init__(self, name=None, version=None):
+        self._imports = ["from conans import ConanFile"]
+        self._name = name
+        self._version = version
+        self._settings = []
+        self._options = {}
+        self._generators = []
+        self._default_options = {}
+        self._provides = []
+        self._deprecated = None
+        self._package_files = {}
+        self._package_files_env = {}
+        self._package_files_link = {}
+        self._build_messages = []
+        self._scm = {}
+        self._requires = []
+        self._requirements = []
+        self._build_requires = []
+        self._build_requirements = []
+        self._revision_mode = None
+        self._package_info = {}
+        self._package_id_lines = []
+        self._test_lines = []
+
+    def with_name(self, name):
+        self._name = name
+        return self
+
+    def with_version(self, version):
+        self._version = version
+        return self
+
+    def with_provides(self, provides):
+        self._provides.append(provides)
+        return self
+
+    def with_deprecated(self, deprecated):
+        self._deprecated = deprecated
+        return self
+
+    def with_revision_mode(self, revision_mode):
+        self._revision_mode = revision_mode
+        return self
+
+    def with_scm(self, scm):
+        self._scm = scm
+        return self
+
+    def with_generator(self, generator):
+        self._generators.append(generator)
+        return self
+
+    def with_require(self, ref, private=False, override=False):
+        ref_str = ref.full_str() if isinstance(ref, ConanFileReference) else ref
+        self._requires.append((ref_str, private, override))
+        return self
+
+    def with_requires(self, *refs):
+        for ref in refs:
+            self.with_require(ref)
+        return self
+
+    def with_requirement(self, ref, private=False, override=False):
+        ref_str = ref.full_str() if isinstance(ref, ConanFileReference) else ref
+        self._requirements.append((ref_str, private, override))
+        return self
+
+    def with_build_requires(self, *refs):
+        for ref in refs:
+            ref_str = ref.full_str() if isinstance(ref, ConanFileReference) else ref
+            self._build_requires.append(ref_str)
+        return self
+
+    def with_build_requirement(self, ref, force_host_context=False):
+        ref_str = ref.full_str() if isinstance(ref, ConanFileReference) else ref
+        self._build_requirements.append((ref_str, force_host_context))
+        return self
+
+    def with_import(self, i):
+        if i not in self._imports:
+            self._imports.append(i)
+        return self
+
+    def with_setting(self, setting):
+        self._settings.append(setting)
+        return self
+
+    def with_settings(self, *settings):
+        self._settings.extend(settings)
+        return self
+
+    def with_option(self, option_name, values):
+        self._options[option_name] = values
+        return self
+
+    def with_default_option(self, option_name, value):
+        self._default_options[option_name] = value
+        return self
+
+    def with_package_file(self, file_name, contents=None, env_var=None, link=None):
+        if not contents and not env_var:
+            raise Exception("Specify contents or env_var")
+        self.with_import("import os")
+        self.with_import("from conans import tools")
+        if contents:
+            self._package_files[file_name] = contents
+        if link:
+            self._package_files_link[file_name] = link
+        if env_var:
+            self._package_files_env[file_name] = env_var
+        return self
+
+    def with_build_msg(self, msg):
+        self._build_messages.append(msg)
+        return self
+
+    def with_package_info(self, cpp_info=None, env_info=None):
+        assert isinstance(cpp_info, dict), "cpp_info ({}) expects dict".format(type(cpp_info))
+        assert isinstance(env_info, dict), "env_info ({}) expects dict".format(type(env_info))
+        if cpp_info:
+            self._package_info["cpp_info"] = cpp_info
+        if env_info:
+            self._package_info["env_info"] = env_info
+        return self
+
+    def with_package_id(self, line):
+        self._package_id_lines.append(line)
+        return self
+
+    def with_test(self, line):
+        self._test_lines.append(line)
+        return self
+
+    @property
+    def _name_line(self):
+        if not self._name:
+            return ""
+        return "name = '{}'".format(self._name)
+
+    @property
+    def _version_line(self):
+        if not self._version:
+            return ""
+        return "version = '{}'".format(self._version)
+
+    @property
+    def _provides_line(self):
+        if not self._provides:
+            return ""
+        line = ", ".join('"{}"'.format(provide) for provide in self._provides)
+        return "provides = {}".format(line)
+
+    @property
+    def _deprecated_line(self):
+        if not self._deprecated:
+            return ""
+        return "deprecated = {}".format(self._deprecated)
+
+    @property
+    def _scm_line(self):
+        if not self._scm:
+            return ""
+        line = ", ".join('"%s": "%s"' % (k, v) for k, v in self._scm.items())
+        return "scm = {%s}" % line
+
+    @property
+    def _generators_line(self):
+        if not self._generators:
+            return ""
+        line = ", ".join('"{}"'.format(generator) for generator in self._generators)
+        return "generators = {}".format(line)
+
+    @property
+    def _revision_mode_line(self):
+        if not self._revision_mode:
+            return ""
+        line = "revision_mode=\"{}\"".format(self._revision_mode)
+        return line
+
+    @property
+    def _settings_line(self):
+        if not self._settings:
+            return ""
+        line = ", ".join('"%s"' % s for s in self._settings)
+        return "settings = {}".format(line)
+
+    @property
+    def _options_line(self):
+        if not self._options:
+            return ""
+        line = ", ".join('"%s": %s' % (k, v) for k, v in self._options.items())
+        tmp = "options = {%s}" % line
+        return tmp
+
+    @property
+    def _default_options_line(self):
+        if not self._default_options:
+            return ""
+        line = ", ".join('"%s": %s' % (k, v) for k, v in self._default_options.items())
+        tmp = "default_options = {%s}" % line
+        return tmp
+
+    @property
+    def _build_requirements_method(self):
+        if not self._build_requirements:
+            return ""
+
+        lines = []
+        for ref, force_host_context in self._build_requirements:
+            force_host = ", force_host_context=True" if force_host_context else ""
+            lines.append('        self.build_requires("{}"{})'.format(ref, force_host))
+        return "def build_requirements(self):\n{}\n".format("\n".join(lines))
+
+    @property
+    def _build_requires_line(self):
+        if not self._build_requires:
+            return ""
+        line = ", ".join(['"{}"'.format(r) for r in self._build_requires])
+        tmp = "build_requires = %s" % line
+        return tmp
+
+    @property
+    def _requires_line(self):
+        if not self._requires:
+            return ""
+        items = []
+        for ref, private, override in self._requires:
+            if private or override:
+                private_str = ", 'private'" if private else ""
+                override_str = ", 'override'" if override else ""
+                items.append('("{}"{}{})'.format(ref, private_str, override_str))
+            else:
+                items.append('"{}"'.format(ref))
+        tmp = "requires = ({}, )".format(", ".join(items))
+        return tmp
+
+    @property
+    def _requirements_method(self):
+        if not self._requirements:
+            return ""
+
+        lines = []
+        for ref, private, override in self._requirements:
+            private_str = ", private=True" if private else ""
+            override_str = ", override=True" if override else ""
+            lines.append('        self.requires("{}"{}{})'.format(ref, private_str, override_str))
+
+        return """
+    def requirements(self):
+{}
+        """.format("\n".join(lines))
+
+    @property
+    def _package_method(self):
+        lines = []
+        if self._package_files:
+            lines = ['        tools.save(os.path.join(self.package_folder, "{}"), "{}")'
+                     ''.format(key, value)
+                     for key, value in self._package_files.items()]
+
+        if self._package_files_env:
+            lines.extend(['        tools.save(os.path.join(self.package_folder, "{}"), '
+                          'os.getenv("{}"))'.format(key, value)
+                          for key, value in self._package_files_env.items()])
+        if self._package_files_link:
+            lines.extend(['        with tools.chdir(os.path.dirname('
+                          'os.path.join(self.package_folder, "{}"))):\n'
+                          '            os.symlink(os.path.basename("{}"), '
+                          'os.path.join(self.package_folder, "{}"))'.format(key, key, value)
+                          for key, value in self._package_files_link.items()])
+
+        if not lines:
+            return ""
+        return """
+    def package(self):
+{}
+    """.format("\n".join(lines))
+
+    @property
+    def _build_method(self):
+        if not self._build_messages:
+            return ""
+        lines = ['        self.output.warn("{}")'.format(m) for m in self._build_messages]
+        return """
+    def build(self):
+{}
+    """.format("\n".join(lines))
+
+    @property
+    def _package_info_method(self):
+        if not self._package_info:
+            return ""
+        lines = []
+        if "cpp_info" in self._package_info:
+            for k, v in self._package_info["cpp_info"].items():
+                if k == "components":
+                    for comp_name, comp in v.items():
+                        for comp_attr_name, comp_attr_value in comp.items():
+                            lines.append('        self.cpp_info.components["{}"].{} = {}'.format(
+                                comp_name, comp_attr_name, str(comp_attr_value)))
+                else:
+                    lines.append('        self.cpp_info.{} = {}'.format(k, str(v)))
+        if "env_info" in self._package_info:
+            for k, v in self._package_info["env_info"].items():
+                lines.append('        self.env_info.{} = {}'.format(k, str(v)))
+
+        return """
+    def package_info(self):
+{}
+        """.format("\n".join(lines))
+
+    @property
+    def _package_id_method(self):
+        if not self._package_id_lines:
+            return ""
+        lines = ['        {}'.format(line) for line in self._package_id_lines]
+        return """
+    def package_id(self):
+{}
+        """.format("\n".join(lines))
+
+    @property
+    def _test_method(self):
+        if not self._test_lines:
+            return ""
+        lines = ['', '    def test(self):'] + ['        %s' % m for m in self._test_lines]
+        return "\n".join(lines)
+
+    def __repr__(self):
+        ret = []
+        ret.extend(self._imports)
+        ret.append("class HelloConan(ConanFile):")
+        if self._name_line:
+            ret.append("    {}".format(self._name_line))
+        if self._version_line:
+            ret.append("    {}".format(self._version_line))
+        if self._provides_line:
+            ret.append("    {}".format(self._provides_line))
+        if self._deprecated_line:
+            ret.append("    {}".format(self._deprecated_line))
+        if self._generators_line:
+            ret.append("    {}".format(self._generators_line))
+        if self._requires_line:
+            ret.append("    {}".format(self._requires_line))
+        if self._build_requires_line:
+            ret.append("    {}".format(self._build_requires_line))
+        if self._requirements_method:
+            ret.append("    {}".format(self._requirements_method))
+        if self._build_requirements_method:
+            ret.append("    {}".format(self._build_requirements_method))
+        if self._scm:
+            ret.append("    {}".format(self._scm_line))
+        if self._revision_mode_line:
+            ret.append("    {}".format(self._revision_mode_line))
+        if self._settings_line:
+            ret.append("    {}".format(self._settings_line))
+        if self._options_line:
+            ret.append("    {}".format(self._options_line))
+        if self._default_options_line:
+            ret.append("    {}".format(self._default_options_line))
+        if self._build_method:
+            ret.append("    {}".format(self._build_method))
+        if self._package_method:
+            ret.append("    {}".format(self._package_method))
+        if self._package_info_method:
+            ret.append("    {}".format(self._package_info_method))
+        if self._package_id_lines:
+            ret.append("    {}".format(self._package_id_method))
+        if self._test_method:
+            ret.append("    {}".format(self._test_method))
+        if len(ret) == 2:
+            ret.append("    pass")
+        return "\n".join(ret)
diff --git a/cpt/test/integration/base.py b/cpt/test/integration/base.py
index f087a942..19829c69 100644
--- a/cpt/test/integration/base.py
+++ b/cpt/test/integration/base.py
@@ -2,10 +2,11 @@
 import unittest
 
 from conans.util.files import mkdir_tmp
+from conans import __version__ as client_version
 
 from conans import tools
 from conans.client.conan_api import ConanAPIV1
-from conans.test.utils.tools import TestBufferConanOutput
+from cpt.test.utils.tools import TestBufferConanOutput
 
 CONAN_UPLOAD_URL = os.getenv("CONAN_UPLOAD_URL",
                              "https://conan.jfrog.io/conan/api/conan/conan-testsuite")
@@ -39,7 +40,10 @@ def save_conanfile(self, conanfile):
 
     def create_project(self):
         with tools.chdir(self.tmp_folder):
-            self.api.new("hello/0.1.0", pure_c=True)
+            if tools.Version(client_version) >= "1.32.0":
+                self.api.new("hello/0.1.0", pure_c=True, exports_sources=True)
+            else:
+                self.api.new("hello/0.1.0")
 
     @property
     def root_project_folder(self):
@@ -50,5 +54,3 @@ def root_project_folder(self):
             else:
                 dir_path = os.path.abspath(os.path.join(dir_path, os.pardir))
         raise Exception("Cannot find root project folder")
-
-
diff --git a/cpt/test/integration/conan_version_test.py b/cpt/test/integration/conan_version_test.py
deleted file mode 100644
index 904438e9..00000000
--- a/cpt/test/integration/conan_version_test.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import unittest
-from cpt.packager import ConanMultiPackager
-
-
-class ConanVersionTest(unittest.TestCase):
-
-    def test_conan_incompatible_version(self):
-        cpt = ConanMultiPackager(username="user", reference="lib/1.0@conan/stable")
-        cpt._newest_supported_conan_version = "2.0"
-        cpt._client_conan_version = "2.1"
-        with self.assertRaisesRegexp(Exception, "Conan/CPT version mismatch. "
-                                                "Conan version installed: 2.1 . "
-                                                "This version of CPT supports only Conan < 2.0"):
-            cpt.run()
-
-        cpt._newest_supported_conan_version = "2.1"
-        cpt._client_conan_version = "2.1"
-        cpt.run()
diff --git a/cpt/test/integration/update_deps_test.py b/cpt/test/integration/update_deps_test.py
index 28d6a869..8b3974b1 100644
--- a/cpt/test/integration/update_deps_test.py
+++ b/cpt/test/integration/update_deps_test.py
@@ -1,7 +1,7 @@
 import unittest
 
 from conans.client.tools import environment_append
-from conans.test.utils.tools import TestClient, TestServer
+from cpt.test.utils.tools import TestClient, TestServer
 from cpt.test.unit.utils import MockCIManager
 from cpt.test.test_client.tools import get_patched_multipackager
 
diff --git a/cpt/test/integration/update_python_reqs_test.py b/cpt/test/integration/update_python_reqs_test.py
index 5f0a177d..90bc7ba2 100644
--- a/cpt/test/integration/update_python_reqs_test.py
+++ b/cpt/test/integration/update_python_reqs_test.py
@@ -1,6 +1,6 @@
 import unittest
 
-from conans.test.utils.tools import TestClient
+from cpt.test.utils.tools import TestClient
 from cpt.test.test_client.tools import get_patched_multipackager
 
 
diff --git a/cpt/test/integration/update_some_deps_test.py b/cpt/test/integration/update_some_deps_test.py
index 803a3ebe..38d48835 100644
--- a/cpt/test/integration/update_some_deps_test.py
+++ b/cpt/test/integration/update_some_deps_test.py
@@ -1,7 +1,7 @@
 import unittest
 
 from conans.client.tools import environment_append
-from conans.test.utils.tools import TestClient, TestServer
+from cpt.test.utils.tools import TestClient, TestServer
 from cpt.test.unit.utils import MockCIManager
 from cpt.test.test_client.tools import get_patched_multipackager
 
diff --git a/cpt/test/integration/upload_test.py b/cpt/test/integration/upload_test.py
index 8ada72e5..98b9f688 100644
--- a/cpt/test/integration/upload_test.py
+++ b/cpt/test/integration/upload_test.py
@@ -1,6 +1,6 @@
 from conans.client import tools
 from conans.errors import ConanException
-from conans.test.utils.test_files import temp_folder
+from cpt.test.utils.test_files import temp_folder
 
 from cpt.test.integration.base import BaseTest
 from cpt.packager import ConanMultiPackager
diff --git a/cpt/test/test_client/config_install_test.py b/cpt/test/test_client/config_install_test.py
index bad3af12..57435873 100644
--- a/cpt/test/test_client/config_install_test.py
+++ b/cpt/test/test_client/config_install_test.py
@@ -3,7 +3,7 @@
 import zipfile
 
 from conans.client.tools import environment_append
-from conans.test.utils.tools import TestClient, TestServer
+from cpt.test.utils.tools import TestClient, TestServer
 
 from cpt.test.test_client.tools import get_patched_multipackager
 
diff --git a/cpt/test/test_client/invalid_config_checks_test.py b/cpt/test/test_client/invalid_config_checks_test.py
index 41aca6b5..3bd6c174 100644
--- a/cpt/test/test_client/invalid_config_checks_test.py
+++ b/cpt/test/test_client/invalid_config_checks_test.py
@@ -1,7 +1,7 @@
 import unittest
 
 from conans.client.tools import environment_append
-from conans.test.utils.tools import TestClient, TestServer
+from cpt.test.utils.tools import TestClient, TestServer
 
 from cpt.test.test_client.tools import get_patched_multipackager
 
diff --git a/cpt/test/test_client/upload_checks_test.py b/cpt/test/test_client/upload_checks_test.py
index a1fe4c52..496f5bf9 100644
--- a/cpt/test/test_client/upload_checks_test.py
+++ b/cpt/test/test_client/upload_checks_test.py
@@ -3,7 +3,7 @@
 import zipfile
 
 from conans.client.tools import environment_append
-from conans.test.utils.tools import TestClient, TestServer
+from cpt.test.utils.tools import TestClient, TestServer
 from cpt.test.unit.utils import MockCIManager
 
 from cpt.test.test_client.tools import get_patched_multipackager
diff --git a/cpt/test/test_client/visual_toolsets_test.py b/cpt/test/test_client/visual_toolsets_test.py
index 4cd3c3bd..66a292de 100644
--- a/cpt/test/test_client/visual_toolsets_test.py
+++ b/cpt/test/test_client/visual_toolsets_test.py
@@ -2,7 +2,7 @@
 import unittest
 
 from conans.client.tools import environment_append
-from conans.test.utils.tools import TestClient, TestServer
+from cpt.test.utils.tools import TestClient, TestServer
 
 from cpt.test.test_client.tools import get_patched_multipackager
 
diff --git a/cpt/test/unit/auth_test.py b/cpt/test/unit/auth_test.py
index 6060bcbb..de7dbdde 100644
--- a/cpt/test/unit/auth_test.py
+++ b/cpt/test/unit/auth_test.py
@@ -1,7 +1,7 @@
 import unittest
 
 from conans import tools
-from conans.test.utils.tools import TestBufferConanOutput
+from cpt.test.utils.tools import TestBufferConanOutput
 from cpt.auth import AuthManager
 from cpt.printer import Printer
 from cpt.test.unit.packager_test import MockConanAPI
diff --git a/cpt/test/unit/ci_manager_test.py b/cpt/test/unit/ci_manager_test.py
index b15b9605..be0c3e6a 100644
--- a/cpt/test/unit/ci_manager_test.py
+++ b/cpt/test/unit/ci_manager_test.py
@@ -1,7 +1,7 @@
 import unittest
 import os
 
-from conans.test.utils.tools import TestBufferConanOutput
+from cpt.test.utils.tools import TestBufferConanOutput
 from cpt.packager import ConanMultiPackager
 from cpt.ci_manager import CIManager
 from conans import tools
diff --git a/cpt/test/unit/packager_test.py b/cpt/test/unit/packager_test.py
index eceb64fa..d4aa97d6 100644
--- a/cpt/test/unit/packager_test.py
+++ b/cpt/test/unit/packager_test.py
@@ -8,7 +8,7 @@
 from cpt.builds_generator import BuildConf
 from cpt.packager import ConanMultiPackager
 from conans import tools
-from conans.test.utils.tools import TestBufferConanOutput
+from cpt.test.utils.tools import TestBufferConanOutput
 from conans.model.ref import ConanFileReference
 from cpt.test.unit.utils import MockConanAPI, MockRunner, MockCIManager
 
diff --git a/cpt/test/unit/utils.py b/cpt/test/unit/utils.py
index 78b83bb2..2de41313 100644
--- a/cpt/test/unit/utils.py
+++ b/cpt/test/unit/utils.py
@@ -5,7 +5,7 @@
 
 from conans import tools
 from conans.model.ref import ConanFileReference
-from conans.test.utils.test_files import temp_folder
+from cpt.test.utils.test_files import temp_folder
 from conans.util.files import save
 from conans.model.version import Version
 from cpt import get_client_version
diff --git a/cpt/test/utils/__init__.py b/cpt/test/utils/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/cpt/test/utils/mocks.py b/cpt/test/utils/mocks.py
new file mode 100644
index 00000000..f4baf7c4
--- /dev/null
+++ b/cpt/test/utils/mocks.py
@@ -0,0 +1,231 @@
+import os
+import sys
+from collections import Counter, defaultdict, namedtuple
+
+
+import six
+from six import StringIO
+
+from conans import ConanFile, Options
+from conans.client.output import ConanOutput
+from conans.client.userio import UserIO
+from conans.model.env_info import DepsEnvInfo, EnvInfo, EnvValues
+from conans.model.options import PackageOptions
+from conans.model.user_info import DepsUserInfo
+
+
+class LocalDBMock(object):
+
+    def __init__(self, user=None, access_token=None, refresh_token=None):
+        self.user = user
+        self.access_token = access_token
+        self.refresh_token = refresh_token
+
+    def get_login(self, _):
+        return self.user, self.access_token, self.refresh_token
+
+    def get_username(self, _):
+        return self.user
+
+    def store(self, user, access_token, refresh_token, _):
+        self.user = user
+        self.access_token = access_token
+        self.refresh_token = refresh_token
+
+
+class MockedUserIO(UserIO):
+    """
+    Mock for testing. If get_username or get_password is requested will raise
+    an exception except we have a value to return.
+    """
+
+    def __init__(self, logins, ins=sys.stdin, out=None):
+        """
+        logins is a dict of {remote: list(user, password)}
+        will return sequentially
+        """
+        assert isinstance(logins, dict)
+        self.logins = logins
+        self.login_index = Counter()
+        UserIO.__init__(self, ins, out)
+
+    def get_username(self, remote_name):
+        username_env = self._get_env_username(remote_name)
+        if username_env:
+            return username_env
+
+        self._raise_if_non_interactive()
+        sub_dict = self.logins[remote_name]
+        index = self.login_index[remote_name]
+        if len(sub_dict) - 1 < index:
+            raise Exception("Bad user/password in testing framework, "
+                            "provide more tuples or input the right ones")
+        return sub_dict[index][0]
+
+    def get_password(self, remote_name):
+        """Overridable for testing purpose"""
+        password_env = self._get_env_password(remote_name)
+        if password_env:
+            return password_env
+
+        self._raise_if_non_interactive()
+        sub_dict = self.logins[remote_name]
+        index = self.login_index[remote_name]
+        tmp = sub_dict[index][1]
+        self.login_index.update([remote_name])
+        return tmp
+
+
+class MockSettings(object):
+
+    def __init__(self, values):
+        self.values = values
+
+    def get_safe(self, value):
+        return self.values.get(value, None)
+
+
+class MockCppInfo(object):
+    def __init__(self):
+        self.bin_paths = []
+        self.lib_paths = []
+        self.include_paths = []
+        self.libs = []
+        self.cflags = []
+        self.cppflags = []
+        self.defines = []
+        self.frameworks = []
+        self.framework_paths = []
+
+
+class MockDepsCppInfo(defaultdict):
+
+    def __init__(self):
+        super(MockDepsCppInfo, self).__init__(MockCppInfo)
+        self.include_paths = []
+        self.lib_paths = []
+        self.libs = []
+        self.defines = []
+        self.cflags = []
+        self.cxxflags = []
+        self.sharedlinkflags = []
+        self.exelinkflags = []
+        self.sysroot = ""
+        self.frameworks = []
+        self.framework_paths = []
+        self.system_libs = []
+
+    @property
+    def deps(self):
+        return self.keys()
+
+
+class MockConanfile(ConanFile):
+
+    def __init__(self, settings, options=None, runner=None):
+        self.deps_cpp_info = MockDepsCppInfo()
+        self.settings = settings
+        self.runner = runner
+        self.options = options or MockOptions({})
+        self.generators = []
+        self.output = TestBufferConanOutput()
+
+        self.should_configure = True
+        self.should_build = True
+        self.should_install = True
+        self.should_test = True
+
+        self.package_folder = None
+
+    def run(self, *args, **kwargs):
+        if self.runner:
+            kwargs["output"] = None
+            self.runner(*args, **kwargs)
+
+
+class ConanFileMock(ConanFile):
+
+    def __init__(self, shared=None, options=None, options_values=None):
+        options = options or ""
+        self.command = None
+        self.path = None
+        self.source_folder = self.build_folder = "."
+        self.settings = None
+        self.options = Options(PackageOptions.loads(options))
+        if options_values:
+            for var, value in options_values.items():
+                self.options._data[var] = value
+        self.deps_cpp_info = MockDepsCppInfo()  # ("deps_cpp_info", "sysroot")("/path/to/sysroot")
+        self.deps_cpp_info.sysroot = "/path/to/sysroot"
+        self.output = TestBufferConanOutput()
+        self.in_local_cache = False
+        self.install_folder = "myinstallfolder"
+        if shared is not None:
+            self.options = namedtuple("options", "shared")(shared)
+        self.should_configure = True
+        self.should_build = True
+        self.should_install = True
+        self.should_test = True
+        self.generators = []
+        self.captured_env = {}
+        self.deps_env_info = DepsEnvInfo()
+        self.env_info = EnvInfo()
+        self.deps_user_info = DepsUserInfo()
+        self._conan_env_values = EnvValues()
+
+    def run(self, command):
+        self.command = command
+        self.path = os.environ["PATH"]
+        self.captured_env = {key: value for key, value in os.environ.items()}
+
+
+MockOptions = MockSettings
+
+
+class TestBufferConanOutput(ConanOutput):
+    """ wraps the normal output of the application, captures it into an stream
+    and gives it operators similar to string, so it can be compared in tests
+    """
+
+    def __init__(self):
+        ConanOutput.__init__(self, StringIO(), color=False)
+
+    def __repr__(self):
+        # FIXME: I'm sure there is a better approach. Look at six docs.
+        if six.PY2:
+            return str(self._stream.getvalue().encode("ascii", "ignore"))
+        else:
+            return self._stream.getvalue()
+
+    def __str__(self, *args, **kwargs):
+        return self.__repr__()
+
+    def __eq__(self, value):
+        return self.__repr__() == value
+
+    def __ne__(self, value):
+        return not self.__eq__(value)
+
+    def __contains__(self, value):
+        return value in self.__repr__()
+
+
+# cli2.0
+class RedirectedTestOutput(StringIO):
+    def __init__(self):
+        super(RedirectedTestOutput, self).__init__()
+
+    def __repr__(self):
+        return self.getvalue()
+
+    def __str__(self, *args, **kwargs):
+        return self.__repr__()
+
+    def __eq__(self, value):
+        return self.__repr__() == value
+
+    def __ne__(self, value):
+        return not self.__eq__(value)
+
+    def __contains__(self, value):
+        return value in self.__repr__()
diff --git a/cpt/test/utils/scm.py b/cpt/test/utils/scm.py
new file mode 100644
index 00000000..4dc8f574
--- /dev/null
+++ b/cpt/test/utils/scm.py
@@ -0,0 +1,126 @@
+import errno
+import os
+import shutil
+import stat
+import subprocess
+import tempfile
+import unittest
+import uuid
+
+from six.moves.urllib.parse import quote
+
+from conans.client.tools import get_cased_path, Git, chdir, SVN
+from cpt.test.utils.test_files import temp_folder
+from conans.util.files import save_files, mkdir
+from conans.util.runners import check_output_runner
+
+
+def create_local_git_repo(files=None, branch=None, submodules=None, folder=None, commits=1,
+                          tags=None, origin_url=None):
+    tmp = folder or temp_folder()
+    tmp = get_cased_path(tmp)
+    if files:
+        save_files(tmp, files)
+    git = Git(tmp)
+    git.run("init .")
+    git.run('config user.email "you@example.com"')
+    git.run('config user.name "Your Name"')
+
+    if branch:
+        git.run("checkout -b %s" % branch)
+
+    git.run("add .")
+    for i in range(0, commits):
+        git.run('commit --allow-empty -m "commiting"')
+
+    tags = tags or []
+    for tag in tags:
+        git.run("tag %s" % tag)
+
+    if submodules:
+        for submodule in submodules:
+            git.run('submodule add "%s"' % submodule)
+        git.run('commit -m "add submodules"')
+
+    if origin_url:
+        git.run('remote add origin {}'.format(origin_url))
+
+    return tmp.replace("\\", "/"), git.get_revision()
+
+
+def create_local_svn_checkout(files, repo_url, rel_project_path=None,
+                              commit_msg='default commit message', delete_checkout=True,
+                              folder=None):
+    tmp_dir = folder or temp_folder()
+    try:
+        rel_project_path = rel_project_path or str(uuid.uuid4())
+        # Do not use SVN class as it is what we will be testing
+        subprocess.check_output('svn co "{url}" "{path}"'.format(url=repo_url,
+                                                                 path=tmp_dir),
+                                shell=True)
+        tmp_project_dir = os.path.join(tmp_dir, rel_project_path)
+        mkdir(tmp_project_dir)
+        save_files(tmp_project_dir, files)
+        with chdir(tmp_project_dir):
+            subprocess.check_output("svn add .", shell=True)
+            subprocess.check_output('svn commit -m "{}"'.format(commit_msg), shell=True)
+            if SVN.get_version() >= SVN.API_CHANGE_VERSION:
+                rev = check_output_runner("svn info --show-item revision").strip()
+            else:
+                import xml.etree.ElementTree as ET
+                output = check_output_runner("svn info --xml").strip()
+                root = ET.fromstring(output)
+                rev = root.findall("./entry")[0].get("revision")
+        project_url = repo_url + "/" + quote(rel_project_path.replace("\\", "/"))
+        return project_url, rev
+    finally:
+        if delete_checkout:
+            shutil.rmtree(tmp_dir, ignore_errors=False, onerror=try_remove_readonly)
+
+
+def create_remote_svn_repo(folder=None):
+    tmp_dir = folder or temp_folder()
+    subprocess.check_output('svnadmin create "{}"'.format(tmp_dir), shell=True)
+    return SVN.file_protocol + quote(tmp_dir.replace("\\", "/"), safe='/:')
+
+
+class SVNLocalRepoTestCase(unittest.TestCase):
+    path_with_spaces = True
+
+    def _create_local_svn_repo(self):
+        folder = os.path.join(self._tmp_folder, 'repo_server')
+        return create_remote_svn_repo(folder)
+
+    def gimme_tmp(self, create=True):
+        tmp = os.path.join(self._tmp_folder, str(uuid.uuid4()))
+        if create:
+            os.makedirs(tmp)
+        return tmp
+
+    def create_project(self, files, rel_project_path=None, commit_msg='default commit message',
+                       delete_checkout=True):
+        tmp_dir = self.gimme_tmp()
+        return create_local_svn_checkout(files, self.repo_url, rel_project_path=rel_project_path,
+                                         commit_msg=commit_msg, delete_checkout=delete_checkout,
+                                         folder=tmp_dir)
+
+    def run(self, *args, **kwargs):
+        tmp_folder = tempfile.mkdtemp(suffix='_conans')
+        try:
+            self._tmp_folder = os.path.join(tmp_folder, 'path with spaces'
+                                            if self.path_with_spaces else 'pathwithoutspaces')
+            os.makedirs(self._tmp_folder)
+            self.repo_url = self._create_local_svn_repo()
+            super(SVNLocalRepoTestCase, self).run(*args, **kwargs)
+        finally:
+            shutil.rmtree(tmp_folder, ignore_errors=False, onerror=try_remove_readonly)
+
+
+def try_remove_readonly(func, path, exc):  # TODO: May promote to conan tools?
+    # src: https://stackoverflow.com/questions/1213706/what-user-do-python-scripts-run-as-in-windows
+    excvalue = exc[1]
+    if func in (os.rmdir, os.remove, os.unlink) and excvalue.errno == errno.EACCES:
+        os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)  # 0777
+        func(path)
+    else:
+        raise OSError("Cannot make read-only %s" % path)
diff --git a/cpt/test/utils/server_launcher.py b/cpt/test/utils/server_launcher.py
new file mode 100644
index 00000000..b273613e
--- /dev/null
+++ b/cpt/test/utils/server_launcher.py
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+import os
+import shutil
+import time
+
+from conans import SERVER_CAPABILITIES
+from conans.server.conf import get_server_store
+from conans.server.crypto.jwt.jwt_credentials_manager import JWTCredentialsManager
+from conans.server.crypto.jwt.jwt_updown_manager import JWTUpDownAuthManager
+from conans.server.migrate import migrate_and_get_server_config
+from conans.server.rest.server import ConanServer
+from conans.server.service.authorize import BasicAuthenticator, BasicAuthorizer
+from cpt.test.utils.test_files import temp_folder
+
+
+TESTING_REMOTE_PRIVATE_USER = "private_user"
+TESTING_REMOTE_PRIVATE_PASS = "private_pass"
+
+
+class TestServerLauncher(object):
+
+    def __init__(self, base_path=None, read_permissions=None,
+                 write_permissions=None, users=None, base_url=None, plugins=None,
+                 server_capabilities=None):
+
+        plugins = plugins or []
+        if not base_path:
+            base_path = temp_folder()
+
+        if not os.path.exists(base_path):
+            raise Exception("Base path not exist! %s")
+
+        self._base_path = base_path
+
+        server_config = migrate_and_get_server_config(base_path)
+        if server_capabilities is None:
+            server_capabilities = set(SERVER_CAPABILITIES)
+
+        # Encode and Decode signature for Upload and Download service
+        updown_auth_manager = JWTUpDownAuthManager(server_config.updown_secret,
+                                                   server_config.authorize_timeout)
+        base_url = base_url or server_config.public_url
+        self.server_store = get_server_store(server_config.disk_storage_path,
+                                             base_url, updown_auth_manager)
+
+        # Prepare some test users
+        if not read_permissions:
+            read_permissions = server_config.read_permissions
+            read_permissions.append(("private_library/1.0.0@private_user/testing", "*"))
+            read_permissions.append(("*/*@*/*", "*"))
+
+        if not write_permissions:
+            write_permissions = server_config.write_permissions
+
+        if not users:
+            users = dict(server_config.users)
+
+        users[TESTING_REMOTE_PRIVATE_USER] = TESTING_REMOTE_PRIVATE_PASS
+
+        authorizer = BasicAuthorizer(read_permissions, write_permissions)
+        authenticator = BasicAuthenticator(users)
+        credentials_manager = JWTCredentialsManager(server_config.jwt_secret,
+                                                    server_config.jwt_expire_time)
+
+        self.port = server_config.port
+        self.ra = ConanServer(self.port, credentials_manager, updown_auth_manager,
+                              authorizer, authenticator, self.server_store,
+                              server_capabilities)
+        for plugin in plugins:
+            self.ra.api_v1.install(plugin)
+            self.ra.api_v2.install(plugin)
+
+    def start(self, daemon=True):
+        """from multiprocessing import Process
+        self.p1 = Process(target=ra.run, kwargs={"host": "0.0.0.0"})
+        self.p1.start()
+        self.p1"""
+        import threading
+
+        class StoppableThread(threading.Thread):
+            """Thread class with a stop() method. The thread itself has to check
+            regularly for the stopped() condition."""
+
+            def __init__(self, *args, **kwargs):
+                super(StoppableThread, self).__init__(*args, **kwargs)
+                self._stop = threading.Event()
+
+            def stop(self):
+                self._stop.set()
+
+            def stopped(self):
+                return self._stop.isSet()
+
+        self.t1 = StoppableThread(target=self.ra.run, kwargs={"host": "0.0.0.0", "quiet": True})
+        self.t1.daemon = daemon
+        self.t1.start()
+        time.sleep(1)
+
+    def stop(self):
+        self.ra.root_app.close()
+        self.t1.stop()
+
+    def clean(self):
+        if os.path.exists(self._base_path):
+            try:
+                shutil.rmtree(self._base_path)
+            except Exception:
+                print("Can't clean the test server data, probably a server process is still opened")
+
+
+if __name__ == "__main__":
+    server = TestServerLauncher()
+    server.start(daemon=False)
diff --git a/cpt/test/utils/test_files.py b/cpt/test/utils/test_files.py
new file mode 100644
index 00000000..cbba03f9
--- /dev/null
+++ b/cpt/test/utils/test_files.py
@@ -0,0 +1,91 @@
+import os
+import platform
+import shutil
+import tarfile
+import tempfile
+
+import time
+from six import BytesIO
+
+from conans.client.tools.files import untargz
+from conans.client.tools.win import get_cased_path
+from conans.errors import ConanException
+from conans.paths import PACKAGE_TGZ_NAME
+from conans.test import CONAN_TEST_FOLDER
+from conans.util.files import gzopen_without_timestamps
+
+
+def wait_until_removed(folder):
+    latest_exception = None
+    for _ in range(50):  # Max 5 seconds
+        time.sleep(0.1)
+        try:
+            shutil.rmtree(folder)
+            break
+        except Exception as e:
+            latest_exception = e
+    else:
+        raise Exception("Could remove folder %s: %s" % (folder, latest_exception))
+
+
+def temp_folder(path_with_spaces=True, create_dir=True):
+    t = tempfile.mkdtemp(suffix='conans', dir=CONAN_TEST_FOLDER)
+    # Make sure that the temp folder is correctly cased, as tempfile return lowercase for Win
+    t = get_cased_path(t)
+    # necessary for Mac OSX, where the temp folders in /var/ are symlinks to /private/var/
+    t = os.path.realpath(t)
+    # FreeBSD and Solaris do not use GNU Make as a the default 'make' program which has trouble
+    # with spaces in paths generated by CMake
+    if not path_with_spaces or platform.system() == "FreeBSD" or platform.system() == "SunOS":
+        path = "pathwithoutspaces"
+    else:
+        path = "path with spaces"
+    nt = os.path.join(t, path)
+    if create_dir:
+        os.makedirs(nt)
+    return nt
+
+
+def uncompress_packaged_files(paths, pref):
+    rev = paths.get_last_revision(pref.ref).revision
+    prev = paths.get_last_package_revision(pref.copy_with_revs(rev, None)).revision
+    pref = pref.copy_with_revs(rev, prev)
+
+    package_path = paths.package(pref)
+    if not(os.path.exists(os.path.join(package_path, PACKAGE_TGZ_NAME))):
+        raise ConanException("%s not found in %s" % (PACKAGE_TGZ_NAME, package_path))
+    tmp = temp_folder()
+    untargz(os.path.join(package_path, PACKAGE_TGZ_NAME), tmp)
+    return tmp
+
+
+def scan_folder(folder):
+    scanned_files = []
+    for root, dirs, files in os.walk(folder):
+        dirs[:] = [d for d in dirs if d != "__pycache__"]
+        relative_path = os.path.relpath(root, folder)
+        for f in files:
+            if f.endswith(".pyc"):
+                continue
+            relative_name = os.path.normpath(os.path.join(relative_path, f)).replace("\\", "/")
+            scanned_files.append(relative_name)
+
+    return sorted(scanned_files)
+
+
+def tgz_with_contents(files):
+    folder = temp_folder()
+    file_path = os.path.join(folder, "myfile.tar.gz")
+
+    with open(file_path, "wb") as tgz_handle:
+        tgz = gzopen_without_timestamps("myfile.tar.gz", mode="w", fileobj=tgz_handle)
+
+        for name, content in files.items():
+            info = tarfile.TarInfo(name=name)
+            data = content.encode('utf-8')
+            info.size = len(data)
+            tgz.addfile(tarinfo=info, fileobj=BytesIO(data))
+
+        tgz.close()
+
+    return file_path
diff --git a/cpt/test/utils/tools.py b/cpt/test/utils/tools.py
new file mode 100644
index 00000000..b406411a
--- /dev/null
+++ b/cpt/test/utils/tools.py
@@ -0,0 +1,898 @@
+import json
+import os
+import random
+import shlex
+import shutil
+import sys
+import textwrap
+import threading
+import time
+import uuid
+from collections import OrderedDict
+from contextlib import contextmanager
+
+import bottle
+import requests
+from mock import Mock
+from requests.exceptions import HTTPError
+from six.moves.urllib.parse import urlsplit, urlunsplit
+from webtest.app import TestApp
+
+from conans import load
+from conans.cli.cli import Cli
+from conans.client.api.conan_api import ConanAPIV2
+from conans.client.cache.cache import ClientCache
+from conans.client.cache.remote_registry import Remotes
+from conans.client.command import Command
+from conans.client.conan_api import Conan
+from conans.client.rest.file_uploader import IterableToFileAdapter
+from conans.client.runner import ConanRunner
+from conans.client.tools import environment_append
+from conans.client.tools.files import replace_in_file
+from conans.errors import NotFoundException, RecipeNotFoundException, PackageNotFoundException
+from conans.model.manifest import FileTreeManifest
+from conans.model.profile import Profile
+from conans.model.ref import ConanFileReference, PackageReference
+from conans.model.settings import Settings
+from conans.server.revision_list import _RevisionEntry
+from cpt.test.assets import copy_assets
+from cpt.test.assets.genconanfile import GenConanfile
+from cpt.test.utils.mocks import MockedUserIO, TestBufferConanOutput
+from cpt.test.utils.scm import create_local_git_repo, create_local_svn_checkout, \
+    create_remote_svn_repo
+from cpt.test.utils.server_launcher import (TESTING_REMOTE_PRIVATE_PASS,
+                                               TESTING_REMOTE_PRIVATE_USER,
+                                               TestServerLauncher)
+from cpt.test.utils.test_files import temp_folder
+from conans.util.conan_v2_mode import CONAN_V2_MODE_ENVVAR
+from conans.util.env_reader import get_env
+from conans.util.files import mkdir, save_files
+
+NO_SETTINGS_PACKAGE_ID = "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9"
+
+ARTIFACTORY_DEFAULT_USER = os.getenv("ARTIFACTORY_DEFAULT_USER", "admin")
+ARTIFACTORY_DEFAULT_PASSWORD = os.getenv("ARTIFACTORY_DEFAULT_PASSWORD", "password")
+ARTIFACTORY_DEFAULT_URL = os.getenv("ARTIFACTORY_DEFAULT_URL", "http://localhost:8090/artifactory")
+
+
+def inc_recipe_manifest_timestamp(cache, reference, inc_time):
+    ref = ConanFileReference.loads(reference)
+    path = cache.package_layout(ref).export()
+    manifest = FileTreeManifest.load(path)
+    manifest.time += inc_time
+    manifest.save(path)
+
+
+def inc_package_manifest_timestamp(cache, package_reference, inc_time):
+    pref = PackageReference.loads(package_reference)
+    path = cache.package_layout(pref.ref).package(pref)
+    manifest = FileTreeManifest.load(path)
+    manifest.time += inc_time
+    manifest.save(path)
+
+
+def test_profile(profile=None, settings=None):
+    if profile is None:
+        profile = Profile()
+    if profile.processed_settings is None:
+        profile.processed_settings = settings or Settings()
+    return profile
+
+
+class TestingResponse(object):
+    """Wraps a response from TestApp external tool
+    to guarantee the presence of response.ok, response.content
+    and response.status_code, as it was a requests library object.
+
+    Is instanced by TestRequester on each request"""
+
+    def __init__(self, test_response):
+        self.test_response = test_response
+
+    def close(self):
+        pass  # Compatibility with close() method of a requests when stream=True
+
+    @property
+    def headers(self):
+        return self.test_response.headers
+
+    @property
+    def ok(self):
+        return self.test_response.status_code == 200
+
+    def raise_for_status(self):
+        """Raises stored :class:`HTTPError`, if one occurred."""
+        http_error_msg = ''
+        if 400 <= self.status_code < 500:
+            http_error_msg = u'%s Client Error: %s' % (self.status_code, self.content)
+
+        elif 500 <= self.status_code < 600:
+            http_error_msg = u'%s Server Error: %s' % (self.status_code, self.content)
+
+        if http_error_msg:
+            raise HTTPError(http_error_msg, response=self)
+
+    @property
+    def content(self):
+        return self.test_response.body
+
+    @property
+    def charset(self):
+        return self.test_response.charset
+
+    @charset.setter
+    def charset(self, newcharset):
+        self.test_response.charset = newcharset
+
+    @property
+    def text(self):
+        return self.test_response.text
+
+    def iter_content(self, chunk_size=1):  # @UnusedVariable
+        return [self.content]
+
+    @property
+    def status_code(self):
+        return self.test_response.status_code
+
+    def json(self):
+        try:
+            return json.loads(self.test_response.content)
+        except:
+            raise ValueError("The response is not a JSON")
+
+
+class TestRequester(object):
+    """Fake requests module calling server applications
+    with TestApp"""
+
+    def __init__(self, test_servers):
+        self.test_servers = test_servers
+
+    @staticmethod
+    def _get_url_path(url):
+        # Remove schema from url
+        _, _, path, query, _ = urlsplit(url)
+        url = urlunsplit(("", "", path, query, ""))
+        return url
+
+    def _get_wsgi_app(self, url):
+        for test_server in self.test_servers.values():
+            if url.startswith(test_server.fake_url):
+                return test_server.app
+
+        raise Exception("Testing error: Not remote found")
+
+    def get(self, url, **kwargs):
+        app, url = self._prepare_call(url, kwargs)
+        if app:
+            response = app.get(url, **kwargs)
+            return TestingResponse(response)
+        else:
+            return requests.get(url, **kwargs)
+
+    def put(self, url, **kwargs):
+        app, url = self._prepare_call(url, kwargs)
+        if app:
+            response = app.put(url, **kwargs)
+            return TestingResponse(response)
+        else:
+            return requests.put(url, **kwargs)
+
+    def delete(self, url, **kwargs):
+        app, url = self._prepare_call(url, kwargs)
+        if app:
+            response = app.delete(url, **kwargs)
+            return TestingResponse(response)
+        else:
+            return requests.delete(url, **kwargs)
+
+    def post(self, url, **kwargs):
+        app, url = self._prepare_call(url, kwargs)
+        if app:
+            response = app.post(url, **kwargs)
+            return TestingResponse(response)
+        else:
+            requests.post(url, **kwargs)
+
+    def _prepare_call(self, url, kwargs):
+        if not url.startswith("http://fake"):  # Call to S3 (or external), perform a real request
+            return None, url
+        app = self._get_wsgi_app(url)
+        url = self._get_url_path(url)  # Remove http://server.com
+
+        self._set_auth_headers(kwargs)
+
+        if app:
+            kwargs["expect_errors"] = True
+            kwargs.pop("stream", None)
+            kwargs.pop("verify", None)
+            kwargs.pop("auth", None)
+            kwargs.pop("cert", None)
+            kwargs.pop("timeout", None)
+            if "data" in kwargs:
+                if isinstance(kwargs["data"], IterableToFileAdapter):
+                    data_accum = b""
+                    for tmp in kwargs["data"]:
+                        data_accum += tmp
+                    kwargs["data"] = data_accum
+                kwargs["params"] = kwargs["data"]
+                del kwargs["data"]  # Parameter in test app is called "params"
+            if kwargs.get("json"):
+                # json is a high level parameter of requests, not a generic one
+                # translate it to data and content_type
+                kwargs["params"] = json.dumps(kwargs["json"])
+                kwargs["content_type"] = "application/json"
+            kwargs.pop("json", None)
+
+        return app, url
+
+    @staticmethod
+    def _set_auth_headers(kwargs):
+        if kwargs.get("auth"):
+            mock_request = Mock()
+            mock_request.headers = {}
+            kwargs["auth"](mock_request)
+            if "headers" not in kwargs:
+                kwargs["headers"] = {}
+            kwargs["headers"].update(mock_request.headers)
+
+
+class ArtifactoryServerStore(object):
+
+    def __init__(self, repo_url, user, password):
+        self._user = user or ARTIFACTORY_DEFAULT_USER
+        self._password = password or ARTIFACTORY_DEFAULT_PASSWORD
+        self._repo_url = repo_url
+
+    @property
+    def _auth(self):
+        return self._user, self._password
+
+    @staticmethod
+    def _root_recipe(ref):
+        return "{}/{}/{}/{}".format(ref.user, ref.name, ref.version, ref.channel)
+
+    @staticmethod
+    def _ref_index(ref):
+        return "{}/index.json".format(ArtifactoryServerStore._root_recipe(ref))
+
+    @staticmethod
+    def _pref_index(pref):
+        tmp = ArtifactoryServerStore._root_recipe(pref.ref)
+        return "{}/{}/package/{}/index.json".format(tmp, pref.ref.revision, pref.id)
+
+    def get_recipe_revisions(self, ref):
+        time.sleep(0.1)  # Index appears to not being updated immediately after a remove
+        url = "{}/{}".format(self._repo_url, self._ref_index(ref))
+        response = requests.get(url, auth=self._auth)
+        response.raise_for_status()
+        the_json = response.json()
+        if not the_json["revisions"]:
+            raise RecipeNotFoundException(ref)
+        tmp = [_RevisionEntry(i["revision"], i["time"]) for i in the_json["revisions"]]
+        return tmp
+
+    def get_package_revisions(self, pref):
+        time.sleep(0.1)  # Index appears to not being updated immediately
+        url = "{}/{}".format(self._repo_url, self._pref_index(pref))
+        response = requests.get(url, auth=self._auth)
+        response.raise_for_status()
+        the_json = response.json()
+        if not the_json["revisions"]:
+            raise PackageNotFoundException(pref)
+        tmp = [_RevisionEntry(i["revision"], i["time"]) for i in the_json["revisions"]]
+        return tmp
+
+    def get_last_revision(self, ref):
+        revisions = self.get_recipe_revisions(ref)
+        return revisions[0]
+
+    def get_last_package_revision(self, ref):
+        revisions = self.get_package_revisions(ref)
+        return revisions[0]
+
+    def package_exists(self, pref):
+        try:
+            if pref.revision:
+                path = self.server_store.package(pref)
+            else:
+                path = self.test_server.server_store.package_revisions_root(pref)
+            return self.test_server.server_store.path_exists(path)
+        except NotFoundException:  # When resolves the latest and there is no package
+            return False
+
+
+class ArtifactoryServer(object):
+
+    def __init__(self, *args, **kwargs):
+        self._user = ARTIFACTORY_DEFAULT_USER
+        self._password = ARTIFACTORY_DEFAULT_PASSWORD
+        self._url = ARTIFACTORY_DEFAULT_URL
+        self._repo_name = "conan_{}".format(str(uuid.uuid4()).replace("-", ""))
+        self.create_repository()
+        self.server_store = ArtifactoryServerStore(self.repo_url, self._user, self._password)
+
+    @property
+    def _auth(self):
+        return self._user, self._password
+
+    @property
+    def repo_url(self):
+        return "{}/{}".format(self._url, self._repo_name)
+
+    @property
+    def repo_api_url(self):
+        return "{}/api/conan/{}".format(self._url, self._repo_name)
+
+    def recipe_revision_time(self, ref):
+        revs = self.server_store.get_recipe_revisions(ref)
+        for r in revs:
+            if r.revision == ref.revision:
+                return r.time
+        return None
+
+    def package_revision_time(self, pref):
+        revs = self.server_store.get_package_revisions(pref)
+        for r in revs:
+            if r.revision == pref.revision:
+                return r.time
+        return None
+
+    def create_repository(self):
+        url = "{}/api/repositories/{}".format(self._url, self._repo_name)
+        config = {"key": self._repo_name, "rclass": "local", "packageType": "conan"}
+        ret = requests.put(url, auth=self._auth, json=config)
+        ret.raise_for_status()
+
+    def package_exists(self, pref):
+        try:
+            revisions = self.server_store.get_package_revisions(pref)
+            if pref.revision:
+                for r in revisions:
+                    if pref.revision == r.revision:
+                        return True
+                return False
+            return True
+        except Exception:  # When resolves the latest and there is no package
+            return False
+
+    def recipe_exists(self, ref):
+        try:
+            revisions = self.server_store.get_recipe_revisions(ref)
+            if ref.revision:
+                for r in revisions:
+                    if ref.revision == r.revision:
+                        return True
+                return False
+            return True
+        except Exception:  # When resolves the latest and there is no package
+            return False
+
+
+class TestServer(object):
+    def __init__(self, read_permissions=None,
+                 write_permissions=None, users=None, plugins=None, base_path=None,
+                 server_capabilities=None, complete_urls=False):
+        """
+             'read_permissions' and 'write_permissions' is a list of:
+                 [("opencv/2.3.4@lasote/testing", "user1, user2")]
+
+             'users':  {username: plain-text-passwd}
+        """
+        # Unique identifier for this server, will be used by TestRequester
+        # to determine where to call. Why? remote_manager just assing an url
+        # to the rest_client, so rest_client doesn't know about object instances,
+        # just urls, so testing framework performs a map between fake urls and instances
+        if read_permissions is None:
+            read_permissions = [("*/*@*/*", "*")]
+        if write_permissions is None:
+            write_permissions = []
+        if users is None:
+            users = {"lasote": "mypass", "conan": "password"}
+
+        self.fake_url = "http://fake%s.com" % str(uuid.uuid4()).replace("-", "")
+        base_url = "%s/v1" % self.fake_url if complete_urls else "v1"
+        self.test_server = TestServerLauncher(base_path, read_permissions,
+                                              write_permissions, users,
+                                              base_url=base_url,
+                                              plugins=plugins,
+                                              server_capabilities=server_capabilities)
+        self.app = TestApp(self.test_server.ra.root_app)
+
+    @property
+    def server_store(self):
+        return self.test_server.server_store
+
+    def __repr__(self):
+        return "TestServer @ " + self.fake_url
+
+    def __str__(self):
+        return self.fake_url
+
+    def recipe_exists(self, ref):
+        try:
+            if not ref.revision:
+                path = self.test_server.server_store.conan_revisions_root(ref)
+            else:
+                path = self.test_server.server_store.base_folder(ref)
+            return self.test_server.server_store.path_exists(path)
+        except NotFoundException:  # When resolves the latest and there is no package
+            return False
+
+    def package_exists(self, pref):
+        try:
+            if pref.revision:
+                path = self.test_server.server_store.package(pref)
+            else:
+                path = self.test_server.server_store.package_revisions_root(pref)
+            return self.test_server.server_store.path_exists(path)
+        except NotFoundException:  # When resolves the latest and there is no package
+            return False
+
+    def latest_recipe(self, ref):
+        rev, _ = self.test_server.server_store.get_last_revision(ref)
+        return ref.copy_with_rev(rev)
+
+    def recipe_revision_time(self, ref):
+        if not ref.revision:
+            raise Exception("Pass a ref with revision (Testing framework)")
+        return self.test_server.server_store.get_revision_time(ref)
+
+    def latest_package(self, pref):
+        if not pref.ref.revision:
+            raise Exception("Pass a pref with .rev.revision (Testing framework)")
+        prev = self.test_server.server_store.get_last_package_revision(pref)
+        return pref.copy_with_revs(pref.ref.revision, prev)
+
+    def package_revision_time(self, pref):
+        if not pref:
+            raise Exception("Pass a pref with revision (Testing framework)")
+        tmp = self.test_server.server_store.get_package_revision_time(pref)
+        return tmp
+
+
+if get_env("CONAN_TEST_WITH_ARTIFACTORY", False):
+    TestServer = ArtifactoryServer
+
+
+def _copy_cache_folder(target_folder):
+    # Some variables affect to cache population (take a different default folder)
+    vars = [CONAN_V2_MODE_ENVVAR, 'CC', 'CXX', 'PATH']
+    cache_key = hash('|'.join(map(str, [os.environ.get(it, None) for it in vars])))
+    master_folder = _copy_cache_folder.master.setdefault(cache_key, temp_folder(create_dir=False))
+    if not os.path.exists(master_folder):
+        # Create and populate the cache folder with the defaults
+        cache = ClientCache(master_folder, TestBufferConanOutput())
+        cache.initialize_config()
+        cache.registry.initialize_remotes()
+        cache.initialize_default_profile()
+        cache.initialize_settings()
+    shutil.copytree(master_folder, target_folder)
+
+
+_copy_cache_folder.master = dict()  # temp_folder(create_dir=False)
+
+
+@contextmanager
+def redirect_output(target):
+    original_stdout = sys.stdout
+    original_stderr = sys.stderr
+    #TODO: change in 2.0
+    # redirecting both of them to the same target for the moment
+    # to assign to Testclient out
+    sys.stdout = target
+    sys.stderr = target
+    try:
+        yield
+    finally:
+        sys.stdout = original_stdout
+        sys.stderr = original_stderr
+
+
+class TestClient(object):
+    """ Test wrap of the conans application to launch tests in the same way as
+    in command line
+    """
+
+    def __init__(self, cache_folder=None, current_folder=None, servers=None, users=None,
+                 requester_class=None, runner=None, path_with_spaces=True,
+                 revisions_enabled=None, cpu_count=1, default_server_user=None,
+                 cache_autopopulate=True):
+        """
+        current_folder: Current execution folder
+        servers: dict of {remote_name: TestServer}
+        logins is a list of (user, password) for auto input in order
+        if required==> [("lasote", "mypass"), ("other", "otherpass")]
+        """
+        if default_server_user is not None:
+            if servers is not None:
+                raise Exception("Cannot define both 'servers' and 'default_server_user'")
+            if users is not None:
+                raise Exception("Cannot define both 'users' and 'default_server_user'")
+            if default_server_user is True:
+                server_users = {"user": "password"}
+                users = {"default": [("user", "password")]}
+            else:
+                server_users = default_server_user
+                users = {"default": list(default_server_user.items())}
+            # Allow write permissions to users
+            server = TestServer(users=server_users, write_permissions=[("*/*@*/*", "*")])
+            servers = {"default": server}
+
+        self.users = users
+        if self.users is None:
+            self.users = {"default": [(TESTING_REMOTE_PRIVATE_USER, TESTING_REMOTE_PRIVATE_PASS)]}
+
+        if cache_autopopulate and (not cache_folder or not os.path.exists(cache_folder)):
+            # Copy a cache folder already populated
+            self.cache_folder = cache_folder or temp_folder(path_with_spaces, create_dir=False)
+            _copy_cache_folder(self.cache_folder)
+        else:
+            self.cache_folder = cache_folder or temp_folder(path_with_spaces)
+
+        self.requester_class = requester_class
+        self.runner = runner
+
+        if servers and len(servers) > 1 and not isinstance(servers, OrderedDict):
+            raise Exception(textwrap.dedent("""
+                Testing framework error: Servers should be an OrderedDict. e.g:
+                    servers = OrderedDict()
+                    servers["r1"] = server
+                    servers["r2"] = TestServer()
+            """))
+
+        self.servers = servers or {}
+        if servers is not False:  # Do not mess with registry remotes
+            self.update_servers()
+        self.current_folder = current_folder or temp_folder(path_with_spaces)
+
+        # Once the client is ready, modify the configuration
+        mkdir(self.current_folder)
+        self.tune_conan_conf(cache_folder, cpu_count, revisions_enabled)
+
+    def load(self, filename):
+        return load(os.path.join(self.current_folder, filename))
+
+    @property
+    def cache(self):
+        # Returns a temporary cache object intended for inspecting it
+        return ClientCache(self.cache_folder, TestBufferConanOutput())
+
+    @property
+    def base_folder(self):
+        # Temporary hack to refactor ConanApp with less changes
+        return self.cache_folder
+
+    @property
+    def storage_folder(self):
+        return self.cache.store
+
+    @property
+    def requester(self):
+        api = self.get_conan_api()
+        api.create_app()
+        return api.app.requester
+
+    @property
+    def proxy(self):
+        api = self.get_conan_api()
+        api.create_app()
+        return api.app.proxy
+
+    @property
+    def _http_requester(self):
+        # Check if servers are real
+        real_servers = any(isinstance(s, (str, ArtifactoryServer))
+                           for s in self.servers.values())
+        if not real_servers:
+            if self.requester_class:
+                return self.requester_class(self.servers)
+            else:
+                return TestRequester(self.servers)
+
+    def _set_revisions(self, value):
+        value = "1" if value else "0"
+        self.run("config set general.revisions_enabled={}".format(value))
+
+    def enable_revisions(self):
+        self._set_revisions(True)
+        assert self.cache.config.revisions_enabled
+
+    def disable_revisions(self):
+        self._set_revisions(False)
+        assert not self.cache.config.revisions_enabled
+
+    def tune_conan_conf(self, cache_folder, cpu_count, revisions_enabled):
+        # Create the default
+        cache = self.cache
+        _ = cache.config
+
+        if cpu_count:
+            replace_in_file(cache.conan_conf_path,
+                            "# cpu_count = 1", "cpu_count = %s" % cpu_count,
+                            output=TestBufferConanOutput(), strict=not bool(cache_folder))
+
+        if revisions_enabled is not None:
+            self._set_revisions(revisions_enabled)
+        elif "TESTING_REVISIONS_ENABLED" in os.environ:
+            value = get_env("TESTING_REVISIONS_ENABLED", True)
+            self._set_revisions(value)
+
+    def update_servers(self):
+        cache = self.cache
+        Remotes().save(cache.remotes_path)
+        registry = cache.registry
+
+        for name, server in self.servers.items():
+            if isinstance(server, ArtifactoryServer):
+                registry.add(name, server.repo_api_url)
+                self.users.update({name: [(ARTIFACTORY_DEFAULT_USER,
+                                           ARTIFACTORY_DEFAULT_PASSWORD)]})
+            elif isinstance(server, TestServer):
+                registry.add(name, server.fake_url)
+            else:
+                registry.add(name, server)
+
+    @contextmanager
+    def chdir(self, newdir):
+        old_dir = self.current_folder
+        if not os.path.isabs(newdir):
+            newdir = os.path.join(old_dir, newdir)
+        mkdir(newdir)
+        self.current_folder = newdir
+        try:
+            yield
+        finally:
+            self.current_folder = old_dir
+
+    def get_conan_api_v2(self):
+        user_io = MockedUserIO(self.users, out=sys.stderr)
+        conan = ConanAPIV2(cache_folder=self.cache_folder, quiet=False, user_io=user_io,
+                           http_requester=self._http_requester, runner=self.runner)
+        return conan
+
+    def get_conan_api_v1(self, user_io=None):
+        if user_io:
+            self.out = user_io.out
+        else:
+            self.out = TestBufferConanOutput()
+        user_io = user_io or MockedUserIO(self.users, out=self.out)
+        conan = Conan(cache_folder=self.cache_folder, output=self.out, user_io=user_io,
+                      http_requester=self._http_requester, runner=self.runner)
+        return conan
+
+    def get_conan_api(self, user_io=None):
+        if os.getenv("CONAN_V2_CLI"):
+            return self.get_conan_api_v2()
+        else:
+            return self.get_conan_api_v1(user_io)
+
+    def run_cli(self, command_line, user_io=None, assert_error=False):
+        conan = self.get_conan_api(user_io)
+        self.api = conan
+        if os.getenv("CONAN_V2_CLI"):
+            command = Cli(conan)
+        else:
+            command = Command(conan)
+        args = shlex.split(command_line)
+        current_dir = os.getcwd()
+        os.chdir(self.current_folder)
+        old_path = sys.path[:]
+        old_modules = list(sys.modules.keys())
+
+        try:
+            error = command.run(args)
+        finally:
+            sys.path = old_path
+            os.chdir(current_dir)
+            # Reset sys.modules to its prev state. A .copy() DOES NOT WORK
+            added_modules = set(sys.modules).difference(old_modules)
+            for added in added_modules:
+                sys.modules.pop(added, None)
+        self._handle_cli_result(command_line, assert_error=assert_error, error=error)
+        return error
+
+    def run(self, command_line, user_io=None, assert_error=False):
+        """ run a single command as in the command line.
+            If user or password is filled, user_io will be mocked to return this
+            tuple if required
+        """
+        # TODO: remove in 2.0
+        if os.getenv("CONAN_V2_CLI"):
+            from cpt.test.utils.mocks import RedirectedTestOutput
+            self.out = RedirectedTestOutput()
+            with redirect_output(self.out):
+                error = self.run_cli(command_line, user_io=user_io, assert_error=assert_error)
+        else:
+            error = self.run_cli(command_line, user_io=user_io, assert_error=assert_error)
+
+        return error
+
+    def run_command(self, command, cwd=None, assert_error=False):
+        output = TestBufferConanOutput()
+        self.out = output
+        runner = ConanRunner(output=output)
+        ret = runner(command, cwd=cwd or self.current_folder)
+        self._handle_cli_result(command, assert_error=assert_error, error=ret)
+        return ret
+
+    def _handle_cli_result(self, command, assert_error, error):
+        if (assert_error and not error) or (not assert_error and error):
+            if assert_error:
+                msg = " Command succeeded (failure expected): "
+            else:
+                msg = " Command failed (unexpectedly): "
+            exc_message = "\n{header}\n{cmd}\n{output_header}\n{output}\n{output_footer}\n".format(
+                header='{:-^80}'.format(msg),
+                output_header='{:-^80}'.format(" Output: "),
+                output_footer='-' * 80,
+                cmd=command,
+                output=self.out
+            )
+            raise Exception(exc_message)
+
+    def save(self, files, path=None, clean_first=False):
+        """ helper metod, will store files in the current folder
+        param files: dict{filename: filecontents}
+        """
+        path = path or self.current_folder
+        if clean_first:
+            shutil.rmtree(self.current_folder, ignore_errors=True)
+        files = {f: str(content) for f, content in files.items()}
+        save_files(path, files)
+        if not files:
+            mkdir(self.current_folder)
+
+    def copy_assets(self, origin_folder, assets=None):
+        copy_assets(origin_folder, self.current_folder, assets)
+
+    # Higher level operations
+    def remove_all(self):
+        self.run("remove '*' -f")
+
+    def export(self, ref, conanfile=GenConanfile(), args=None):
+        """ export a ConanFile with as "ref" and return the reference with recipe revision
+        """
+        if conanfile:
+            self.save({"conanfile.py": conanfile})
+        self.run("export . {} {}".format(ref.full_str(), args or ""))
+        rrev = self.cache.package_layout(ref).recipe_revision()
+        return ref.copy_with_rev(rrev)
+
+    def init_git_repo(self, files=None, branch=None, submodules=None, folder=None, origin_url=None):
+        if folder is not None:
+            folder = os.path.join(self.current_folder, folder)
+        else:
+            folder = self.current_folder
+        _, commit = create_local_git_repo(files, branch, submodules, folder=folder,
+                                          origin_url=origin_url)
+        return commit
+
+
+class TurboTestClient(TestClient):
+    tmp_json_name = ".tmp_json"
+
+    def __init__(self, *args, **kwargs):
+        if "users" not in kwargs and "default_server_user" not in kwargs:
+            from collections import defaultdict
+            kwargs["users"] = defaultdict(lambda: [("conan", "password")])
+
+        super(TurboTestClient, self).__init__(*args, **kwargs)
+
+    def create(self, ref, conanfile=GenConanfile(), args=None, assert_error=False):
+        if conanfile:
+            self.save({"conanfile.py": conanfile})
+        full_str = "{}@".format(ref.full_str()) if not ref.user else ref.full_str()
+        self.run("create . {} {} --json {}".format(full_str,
+                                                   args or "", self.tmp_json_name),
+                 assert_error=assert_error)
+        rrev = self.cache.package_layout(ref).recipe_revision()
+        data = json.loads(self.load(self.tmp_json_name))
+        if assert_error:
+            return None
+        package_id = data["installed"][0]["packages"][0]["id"]
+        package_ref = PackageReference(ref, package_id)
+        prev = self.cache.package_layout(ref.copy_clear_rev()).package_revision(package_ref)
+        return package_ref.copy_with_revs(rrev, prev)
+
+    def upload_all(self, ref, remote=None, args=None, assert_error=False):
+        remote = remote or list(self.servers.keys())[0]
+        self.run("upload {} -c --all -r {} {}".format(ref.full_str(), remote, args or ""),
+                 assert_error=assert_error)
+        if not assert_error:
+            remote_rrev, _ = self.servers[remote].server_store.get_last_revision(ref)
+            return ref.copy_with_rev(remote_rrev)
+        return
+
+    def export_pkg(self, ref, conanfile=GenConanfile(), args=None, assert_error=False):
+        if conanfile:
+            self.save({"conanfile.py": conanfile})
+        self.run("export-pkg . {} {} --json {}".format(ref.full_str(),
+                                                       args or "", self.tmp_json_name),
+                 assert_error=assert_error)
+        rrev = self.cache.package_layout(ref).recipe_revision()
+        data = json.loads(self.load(self.tmp_json_name))
+        if assert_error:
+            return None
+        package_id = data["installed"][0]["packages"][0]["id"]
+        package_ref = PackageReference(ref, package_id)
+        prev = self.cache.package_layout(ref.copy_clear_rev()).package_revision(package_ref)
+        return package_ref.copy_with_revs(rrev, prev)
+
+    def recipe_exists(self, ref):
+        return self.cache.package_layout(ref).recipe_exists()
+
+    def package_exists(self, pref):
+        return self.cache.package_layout(pref.ref).package_exists(pref)
+
+    def recipe_revision(self, ref):
+        return self.cache.package_layout(ref).recipe_revision()
+
+    def package_revision(self, pref):
+        return self.cache.package_layout(pref.ref).package_revision(pref)
+
+    def search(self, pattern, remote=None, assert_error=False, args=None):
+        remote = " -r={}".format(remote) if remote else ""
+        self.run("search {} --json {} {} {}".format(pattern, self.tmp_json_name, remote,
+                                                    args or ""),
+                 assert_error=assert_error)
+        data = json.loads(self.load(self.tmp_json_name))
+        return data
+
+    def massive_uploader(self, ref, revisions, num_prev, remote=None):
+        """Uploads N revisions with M package revisions. The revisions can be specified like:
+            revisions = [{"os": "Windows"}, {"os": "Linux"}], \
+                        [{"os": "Macos"}], \
+                        [{"os": "Solaris"}, {"os": "FreeBSD"}]
+
+            IMPORTANT: Different settings keys will cause different recipe revisions
+        """
+        remote = remote or "default"
+        ret = []
+        for i, settings_groups in enumerate(revisions):
+            tmp = []
+            for settings in settings_groups:
+                conanfile_gen = GenConanfile(). \
+                    with_build_msg("REV{}".format(i)). \
+                    with_package_file("file", env_var="MY_VAR")
+                for s in settings.keys():
+                    conanfile_gen = conanfile_gen.with_setting(s)
+                for k in range(num_prev):
+                    args = " ".join(["-s {}={}".format(key, value)
+                                     for key, value in settings.items()])
+                    with environment_append({"MY_VAR": str(k)}):
+                        pref = self.create(ref, conanfile=conanfile_gen, args=args)
+                        self.upload_all(ref, remote=remote)
+                        tmp.append(pref)
+                ret.append(tmp)
+        return ret
+
+    def init_svn_repo(self, subpath, files=None, repo_url=None):
+        if not repo_url:
+            repo_url = create_remote_svn_repo(temp_folder())
+        _, rev = create_local_svn_checkout(files, repo_url, folder=self.current_folder,
+                                           rel_project_path=subpath, delete_checkout=False)
+        return rev
+
+
+class StoppableThreadBottle(threading.Thread):
+    """
+    Real server to test download endpoints
+    """
+
+    def __init__(self, host=None, port=None):
+        self.host = host or "127.0.0.1"
+        self.port = port or random.randrange(48000, 49151)
+        self.server = bottle.Bottle()
+        super(StoppableThreadBottle, self).__init__(target=self.server.run,
+                                                    kwargs={"host": self.host, "port": self.port})
+        self.daemon = True
+        self._stop = threading.Event()
+
+    def stop(self):
+        self._stop.set()
+
+    def run_server(self):
+        self.start()
+        time.sleep(1)

From 96303deac5e5ac8a41c57a4e33e9f9f4c4d5906a Mon Sep 17 00:00:00 2001
From: theirix <theirix@gmail.com>
Date: Wed, 21 Oct 2020 13:01:33 +0300
Subject: [PATCH 11/26] Pass cwd to DockerCreateRunner, #447

The patch allows building packages using Docker in non-flat repositories
with specifying cwd.

Example:

    builder = ConanMultiPackager(cwd=os.path.join(os.getcwd(), 'recipes', 'foobar'))
---
 cpt/packager.py                |  3 ++-
 cpt/runner.py                  |  6 ++++--
 cpt/test/unit/packager_test.py | 16 ++++++++++++++++
 3 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/cpt/packager.py b/cpt/packager.py
index 7f310715..e65460d8 100644
--- a/cpt/packager.py
+++ b/cpt/packager.py
@@ -704,7 +704,8 @@ def run_builds(self, curpage=None, total_pages=None, base_profile_name=None):
                                        lockfile=self.lockfile,
                                        force_selinux=self.force_selinux,
                                        skip_recipe_export=skip_recipe_export,
-                                       update_dependencies=self.update_dependencies)
+                                       update_dependencies=self.update_dependencies,
+                                       cwd=self.cwd)
 
                 r.run(pull_image=not pulled_docker_images[docker_image],
                       docker_entry_script=self.docker_entry_script)
diff --git a/cpt/runner.py b/cpt/runner.py
index 42769ccd..6d72f15e 100644
--- a/cpt/runner.py
+++ b/cpt/runner.py
@@ -184,7 +184,8 @@ def __init__(self, profile_text, base_profile_text, base_profile_name, reference
                  force_selinux=None,
                  skip_recipe_export=False,
                  update_dependencies=False,
-                 lockfile=None):
+                 lockfile=None,
+                 cwd=None):
 
         self.printer = printer or Printer()
         self._upload = upload
@@ -219,6 +220,7 @@ def __init__(self, profile_text, base_profile_text, base_profile_name, reference
         self._force_selinux = force_selinux
         self._skip_recipe_export = skip_recipe_export
         self._update_dependencies = update_dependencies
+        self._cwd = cwd or os.getcwd()
 
     def _pip_update_conan_command(self):
         commands = []
@@ -298,7 +300,7 @@ def run(self, pull_image=True, docker_entry_script=None):
         command = ('%s docker run --rm -v "%s:%s/project%s" %s %s %s %s %s '
                    '"%s cd project && '
                    '%s run_create_in_docker "' % (self._sudo_docker_command,
-                                                  os.getcwd(),
+                                                  self._cwd,
                                                   self._docker_conan_home,
                                                   volume_options,
                                                   env_vars_text,
diff --git a/cpt/test/unit/packager_test.py b/cpt/test/unit/packager_test.py
index d4aa97d6..548e9d9e 100644
--- a/cpt/test/unit/packager_test.py
+++ b/cpt/test/unit/packager_test.py
@@ -1078,3 +1078,19 @@ def test_lockfile(self):
             builder.add_common_builds()
             builder.run()
             self.assertEquals("couse.lock", self.conan_api.calls[-1].kwargs["lockfile"])
+
+    def test_docker_cwd(self):
+        cwd = os.path.join(os.getcwd(), 'subdir')
+        self.packager = ConanMultiPackager(username="lasote",
+                                           channel="mychannel",
+                                           runner=self.runner,
+                                           conan_api=self.conan_api,
+                                           gcc_versions=["9"],
+                                           use_docker=True,
+                                           reference="zlib/1.2.11",
+                                           ci_manager=self.ci_manager,
+                                           cwd=cwd)
+
+        self._add_build(1, "gcc", "9")
+        self.packager.run_builds(1, 1)
+        self.assertIn('docker run --rm -v "%s:/home/conan/project"' % cwd, self.runner.calls[4])
\ No newline at end of file

From 06696a34dab372825655ca6f6bd7980113ecc249 Mon Sep 17 00:00:00 2001
From: theirix <theirix@gmail.com>
Date: Fri, 30 Oct 2020 21:28:39 +0300
Subject: [PATCH 12/26] Fix test_docker_cwd on Windows, #447

---
 cpt/test/unit/packager_test.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cpt/test/unit/packager_test.py b/cpt/test/unit/packager_test.py
index 548e9d9e..cd5afab3 100644
--- a/cpt/test/unit/packager_test.py
+++ b/cpt/test/unit/packager_test.py
@@ -1093,4 +1093,4 @@ def test_docker_cwd(self):
 
         self._add_build(1, "gcc", "9")
         self.packager.run_builds(1, 1)
-        self.assertIn('docker run --rm -v "%s:/home/conan/project"' % cwd, self.runner.calls[4])
\ No newline at end of file
+        self.assertIn('docker run --rm -v "%s:%s/project"' % (cwd, self.packager.docker_conan_home), self.runner.calls[4])

From 6e32035927278275dd23e3ebbc97a2500caa85b1 Mon Sep 17 00:00:00 2001
From: Kasun Hewage <kasun.ch@gmail.com>
Date: Thu, 19 Nov 2020 15:40:05 +0100
Subject: [PATCH 13/26] Hide sensitive information from getting
 displayed/logged.

---
 cpt/runner.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/cpt/runner.py b/cpt/runner.py
index 6d72f15e..431617ce 100644
--- a/cpt/runner.py
+++ b/cpt/runner.py
@@ -1,6 +1,7 @@
 import os
 import sys
 import subprocess
+import re
 from collections import namedtuple
 
 from conans import tools
@@ -376,7 +377,11 @@ def __init__(self, runner, printer):
         self.printer = printer
 
     def __call__(self, command):
-        self.printer.print_command(command)
+        cmd_str = command
+        if hide_sensitive:
+            cmd_str = re.sub(r'(CONAN_LOGIN_USERNAME[_\w+]*)=\"(\w+)\"', r'\1="xxxxxxxx"', cmd_str)
+            cmd_str = re.sub(r'(CONAN_PASSWORD[_\w+]*)=\"(\w+)\"', r'\1="xxxxxxxx"', cmd_str)
+        self.printer.print_command(cmd_str)
         sys.stderr.flush()
         sys.stdout.flush()
         return self.runner(command)

From a6459bd636363fa22572e067f75c04d7f4a5c79e Mon Sep 17 00:00:00 2001
From: Kasun Hewage <kasun.ch@gmail.com>
Date: Thu, 19 Nov 2020 17:32:56 +0100
Subject: [PATCH 14/26] Added the missing function argument.

---
 cpt/runner.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cpt/runner.py b/cpt/runner.py
index 431617ce..65c10997 100644
--- a/cpt/runner.py
+++ b/cpt/runner.py
@@ -376,7 +376,7 @@ def __init__(self, runner, printer):
         self.runner = runner
         self.printer = printer
 
-    def __call__(self, command):
+    def __call__(self, command, hide_sensitive=True):
         cmd_str = command
         if hide_sensitive:
             cmd_str = re.sub(r'(CONAN_LOGIN_USERNAME[_\w+]*)=\"(\w+)\"', r'\1="xxxxxxxx"', cmd_str)

From 31109723fd0253846d3bad2ffa5959667336ad36 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Mon, 30 Nov 2020 12:49:28 -0300
Subject: [PATCH 15/26] Validate hide sensitive data

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/test/integration/docker_test.py | 33 +++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/cpt/test/integration/docker_test.py b/cpt/test/integration/docker_test.py
index b587088a..683bee20 100644
--- a/cpt/test/integration/docker_test.py
+++ b/cpt/test/integration/docker_test.py
@@ -1,6 +1,7 @@
 import subprocess
 import unittest
 import time
+import textwrap
 
 from conans import tools
 from conans.model.ref import ConanFileReference
@@ -234,3 +235,35 @@ def build(self):
                 self.packager.run()
                 self.assertIn("Error updating the image", str(raised.exception))
                 self.assertIn("foobar install conan_package_tools", str(raised.exception))
+
+    @unittest.skipUnless(is_linux_and_have_docker(), "Requires Linux and Docker")
+    def test_docker_hidden_password(self):
+        conanfile = textwrap.dedent("""
+                from conans import ConanFile
+
+                class Pkg(ConanFile):
+                    settings = "os", "compiler", "build_type", "arch"
+
+                    def build(self):
+                        pass
+            """)
+
+        self.save_conanfile(conanfile)
+        with tools.environment_append({"CONAN_USERNAME": "bar",
+                                       "CONAN_LOGIN_USERNAME": "foobar",
+                                       "CONAN_PASSWORD": "foobazcouse",
+                                       "CONAN_DOCKER_IMAGE": "conanio/gcc8",
+                                       "CONAN_REFERENCE": "foo/0.0.1@bar/testing",
+                                       "CONAN_DOCKER_IMAGE_SKIP_UPDATE": "TRUE",
+                                       "CONAN_FORCE_SELINUX": "TRUE",
+                                       "CONAN_DOCKER_USE_SUDO": "FALSE",
+                                       "CONAN_DOCKER_SHELL": "/bin/bash -c",
+                                       }):
+            self.packager = ConanMultiPackager(gcc_versions=["8"],
+                                               archs=["x86_64"],
+                                               build_types=["Release"],
+                                               out=self.output.write)
+            self.packager.add({})
+            self.packager.run()
+            self.assertIn('-e CONAN_LOGIN_USERNAME="xxxxxxxx"', self.output)
+            self.assertIn('-e CONAN_PASSWORD="xxxxxxxx"', self.output)

From d156da13f4dcdc24791d528465fb03717b35c873 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Wed, 2 Dec 2020 10:02:58 -0300
Subject: [PATCH 16/26] Avoid import error on Conan 1.32

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 {conan => conanio}/__init__.py | 0
 {conan => conanio}/packager.py | 0
 2 files changed, 0 insertions(+), 0 deletions(-)
 rename {conan => conanio}/__init__.py (100%)
 rename {conan => conanio}/packager.py (100%)

diff --git a/conan/__init__.py b/conanio/__init__.py
similarity index 100%
rename from conan/__init__.py
rename to conanio/__init__.py
diff --git a/conan/packager.py b/conanio/packager.py
similarity index 100%
rename from conan/packager.py
rename to conanio/packager.py

From 60e495b34427e9cbfaa241f9f3fe39f204b017c5 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Wed, 2 Dec 2020 10:19:27 -0300
Subject: [PATCH 17/26] Test devel first

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 .travis.yml | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 55a7c3c5..5b0501b7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,15 @@ jobs:
   fast_finish: true
   include:
 
+    - stage: Conan Development - Linux
+      if: branch != master AND branch !~ /release*/
+      python: 2.7
+      env: TOXENV=py27-conan-dev
+    - python: 3.7
+      if: branch != master AND branch !~ /release*/
+      env: TOXENV=py37-conan-dev
+      dist: xenial
+
     - stage: Conan Latest - Linux
       python: 2.7
       env: TOXENV=py27-conan-latest
@@ -23,16 +32,6 @@ jobs:
       osx_image: xcode10.3
       env: PYVER=py37 TOXENV=py37-conan-latest
 
-    - stage: Conan Development - Linux
-      if: branch != master AND branch !~ /release*/
-      python: 2.7
-      env: TOXENV=py27-conan-dev
-    - python: 3.7
-      if: branch != master AND branch !~ /release*/
-      env: TOXENV=py37-conan-dev
-      dist: xenial
-
-
 
 install:
   - .ci/travis/install.sh
@@ -41,4 +40,4 @@ script:
   - .ci/travis/run.sh
 
 after_success:
-  - bash <(curl -s https://codecov.io/bash)
\ No newline at end of file
+  - bash <(curl -s https://codecov.io/bash)

From 4f2e55450446b9ed758f3be5d70f799dacfd097d Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Wed, 2 Dec 2020 10:20:39 -0300
Subject: [PATCH 18/26] Add developmen test on Windows

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 appveyor.yml | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/appveyor.yml b/appveyor.yml
index 3f7abf42..a6f4ab2c 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,5 +1,15 @@
 environment:
   matrix:
+    - PYTHON: "C:\\Python27"
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+      USE_UNSUPPORTED_CONAN_WITH_PYTHON_2: "1"
+      TOXENV: "py27-conan-dev"
+
+    - PYTHON: "C:\\Python37"
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+      USE_UNSUPPORTED_CONAN_WITH_PYTHON_2: "1"
+      TOXENV: "py37-conan-dev"
+
     - PYTHON: "C:\\Python27"
       APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
       USE_UNSUPPORTED_CONAN_WITH_PYTHON_2: "1"

From 776861eeed4244185592f8bda6dea4cb5540423d Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Thu, 3 Dec 2020 08:40:50 -0300
Subject: [PATCH 19/26] Update development verstion to 0.35.0

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cpt/__init__.py b/cpt/__init__.py
index eccc02cf..7c59b0d3 100644
--- a/cpt/__init__.py
+++ b/cpt/__init__.py
@@ -1,5 +1,5 @@
 
-__version__ = '0.34.5-dev'
+__version__ = '0.35.0-dev'
 
 
 def get_client_version():

From 6f4758b39c257dcabcabc6405cf400e8f6a358ea Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Thu, 3 Dec 2020 08:50:25 -0300
Subject: [PATCH 20/26] Update develop version to 0.36.0

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cpt/__init__.py b/cpt/__init__.py
index 7c59b0d3..a047d1c6 100644
--- a/cpt/__init__.py
+++ b/cpt/__init__.py
@@ -1,5 +1,5 @@
 
-__version__ = '0.35.0-dev'
+__version__ = '0.36.0-dev'
 
 
 def get_client_version():

From 485e4f920daad93a736244e00fa65d5f630cc8d5 Mon Sep 17 00:00:00 2001
From: Artalus <artalus-mail@yandex.ru>
Date: Tue, 10 Mar 2020 12:21:24 +0300
Subject: [PATCH 21/26] pass CONAN_USERNAME envvar to docker (#479)

---
 cpt/runner.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cpt/runner.py b/cpt/runner.py
index 65c10997..8467050b 100644
--- a/cpt/runner.py
+++ b/cpt/runner.py
@@ -338,7 +338,7 @@ def get_env_vars(self):
         ret["CPT_BASE_PROFILE"] = escape_env(self._base_profile_text)
         ret["CPT_BASE_PROFILE_NAME"] = escape_env(self._base_profile_name)
 
-        ret["CONAN_USERNAME"] = escape_env(self._reference.user)
+        ret["CONAN_USERNAME"] = escape_env(self._reference.user or ret.get("CONAN_USERNAME"))
         ret["CONAN_TEMP_TEST_FOLDER"] = "1"  # test package folder to a temp one
         ret["CPT_UPLOAD_ENABLED"] = self._upload
         ret["CPT_UPLOAD_RETRY"] = self._upload_retry

From 08768f2b8a034af316a0cf0a2bf4e07789fcc0d0 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Mon, 15 Mar 2021 12:04:13 -0300
Subject: [PATCH 22/26] #479 Validate hotfix for _ username

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/test/integration/docker_test.py | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/cpt/test/integration/docker_test.py b/cpt/test/integration/docker_test.py
index 683bee20..b22840b7 100644
--- a/cpt/test/integration/docker_test.py
+++ b/cpt/test/integration/docker_test.py
@@ -267,3 +267,32 @@ def build(self):
             self.packager.run()
             self.assertIn('-e CONAN_LOGIN_USERNAME="xxxxxxxx"', self.output)
             self.assertIn('-e CONAN_PASSWORD="xxxxxxxx"', self.output)
+
+    @unittest.skipUnless(is_linux_and_have_docker(), "Requires Linux and Docker")
+    def test_docker_underscore_user_channel(self):
+        conanfile = textwrap.dedent("""
+                from conans import ConanFile
+
+                class Pkg(ConanFile):
+                    def build(self):
+                        pass
+            """)
+
+        self.save_conanfile(conanfile)
+        with tools.environment_append({"CONAN_USERNAME": "_",
+                                       "CONAN_CHANNEL": "_",
+                                       "CONAN_DOCKER_IMAGE": "conanio/gcc8",
+                                       "CONAN_REFERENCE": "foo/0.0.1",
+                                       "CONAN_DOCKER_IMAGE_SKIP_UPDATE": "TRUE",
+                                       "CONAN_FORCE_SELINUX": "TRUE",
+                                       "CONAN_DOCKER_USE_SUDO": "FALSE",
+                                       "CONAN_DOCKER_SHELL": "/bin/bash -c",
+                                       }):
+            self.packager = ConanMultiPackager(gcc_versions=["8"],
+                                               archs=["x86_64"],
+                                               build_types=["Release"],
+                                               out=self.output.write)
+            self.packager.add({})
+            self.packager.run()
+            self.assertIn('e CONAN_USERNAME="_"', self.output)
+            self.assertIn('-e CONAN_CHANNEL="_"', self.output)

From 8630068a175b502469c0c29b3a6375314bef5f64 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Mon, 15 Mar 2021 16:35:31 -0300
Subject: [PATCH 23/26] #479 Improve env var for
 test_docker_underscore_user_channel

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/test/integration/docker_test.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cpt/test/integration/docker_test.py b/cpt/test/integration/docker_test.py
index b22840b7..c30e8e50 100644
--- a/cpt/test/integration/docker_test.py
+++ b/cpt/test/integration/docker_test.py
@@ -294,5 +294,5 @@ def build(self):
                                                out=self.output.write)
             self.packager.add({})
             self.packager.run()
-            self.assertIn('e CONAN_USERNAME="_"', self.output)
+            self.assertIn('-e CONAN_USERNAME="_"', self.output)
             self.assertIn('-e CONAN_CHANNEL="_"', self.output)

From c533ea8cbec2e500b7d87404f649025ef6a989ee Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Mon, 15 Mar 2021 17:32:02 -0300
Subject: [PATCH 24/26] #479 Add Docker pull retry

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/runner.py | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/cpt/runner.py b/cpt/runner.py
index 8467050b..fc663da1 100644
--- a/cpt/runner.py
+++ b/cpt/runner.py
@@ -325,9 +325,14 @@ def run(self, pull_image=True, docker_entry_script=None):
 
     def pull_image(self):
         with self.printer.foldable_output("docker pull"):
-            ret = self._runner("%s docker pull %s" % (self._sudo_docker_command, self._docker_image))
-            if ret != 0:
-                raise Exception("Error pulling the image: %s" % self._docker_image)
+            for retry in range(1, 4):
+                ret = self._runner("%s docker pull %s" % (self._sudo_docker_command, self._docker_image))
+                if ret == 0:
+                    break
+                elif retry == 3:
+                    raise Exception("Error pulling the image: %s" % self._docker_image)
+                self.printer.print_message("Could not pull docker image '{}'. Retry ({})"
+                                           .format(self._docker_image, retry))
 
     def get_env_vars(self):
         ret = {key: value for key, value in os.environ.items() if key.startswith("CONAN_") and

From 67cfe3a38d3ebfbf9889452494f20c5130af2871 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Mon, 15 Mar 2021 17:33:23 -0300
Subject: [PATCH 25/26] #479 Add small interval for docker pull retry

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/runner.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/cpt/runner.py b/cpt/runner.py
index fc663da1..ab08fa60 100644
--- a/cpt/runner.py
+++ b/cpt/runner.py
@@ -2,6 +2,7 @@
 import sys
 import subprocess
 import re
+import time
 from collections import namedtuple
 
 from conans import tools
@@ -333,6 +334,7 @@ def pull_image(self):
                     raise Exception("Error pulling the image: %s" % self._docker_image)
                 self.printer.print_message("Could not pull docker image '{}'. Retry ({})"
                                            .format(self._docker_image, retry))
+                time.sleep(3)
 
     def get_env_vars(self):
         ret = {key: value for key, value in os.environ.items() if key.startswith("CONAN_") and

From 16f0a3dc0ebce775769320f318a4c181b4fef127 Mon Sep 17 00:00:00 2001
From: Uilian Ries <uilianries@gmail.com>
Date: Mon, 15 Mar 2021 17:41:51 -0300
Subject: [PATCH 26/26] #479 Fix unexpected indent for docker tests

Signed-off-by: Uilian Ries <uilianries@gmail.com>
---
 cpt/test/integration/docker_test.py | 46 +++++++++++++++--------------
 1 file changed, 24 insertions(+), 22 deletions(-)

diff --git a/cpt/test/integration/docker_test.py b/cpt/test/integration/docker_test.py
index c30e8e50..2708b349 100644
--- a/cpt/test/integration/docker_test.py
+++ b/cpt/test/integration/docker_test.py
@@ -34,13 +34,13 @@ def test_docker(self):
         client_version = get_client_version()
         ci_manager = MockCIManager()
         unique_ref = "zlib/%s" % str(time.time())
-        conanfile = """from conans import ConanFile
-import os
-
-class Pkg(ConanFile):
-    settings = "os", "compiler", "build_type", "arch"
+        conanfile = textwrap.dedent("""
+                from conans import ConanFile
+                import os
 
-"""
+                class Pkg(ConanFile):
+                    settings = "os", "compiler", "build_type", "arch"
+            """)
 
         self.save_conanfile(conanfile)
         with tools.environment_append({"CONAN_DOCKER_RUN_OPTIONS": "--network=host -v{}:/tmp/cpt".format(self.root_project_folder),
@@ -116,16 +116,17 @@ class Pkg(ConanFile):
 
     @unittest.skipUnless(is_linux_and_have_docker(), "Requires Linux and Docker")
     def test_docker_run_options(self):
-        conanfile = """from conans import ConanFile
-import os
+        conanfile = textwrap.dedent("""
+                from conans import ConanFile
+                import os
 
-class Pkg(ConanFile):
-    settings = "os", "compiler", "build_type", "arch"
-    requires = "zlib/1.2.11@conan/stable"
+                class Pkg(ConanFile):
+                    settings = "os", "compiler", "build_type", "arch"
+                    requires = "zlib/1.2.11@conan/stable"
 
-    def build(self):
-        pass
-"""
+                    def build(self):
+                        pass
+            """)
         self.save_conanfile(conanfile)
         # Validate by Environemnt Variable
         with tools.environment_append({"CONAN_DOCKER_ENTRY_SCRIPT": "pip install -U /tmp/cpt",
@@ -204,16 +205,17 @@ def test_docker_run_android(self):
 
     @unittest.skipUnless(is_linux_and_have_docker(), "Requires Linux and Docker")
     def test_docker_custom_pip_command(self):
-        conanfile = """from conans import ConanFile
-        import os
+        conanfile = textwrap.dedent("""
+                from conans import ConanFile
+                import os
 
-        class Pkg(ConanFile):
-            settings = "os", "compiler", "build_type", "arch"
-            requires = "zlib/1.2.11@conan/stable"
+                class Pkg(ConanFile):
+                    settings = "os", "compiler", "build_type", "arch"
+                    requires = "zlib/1.2.11@conan/stable"
 
-            def build(self):
-                pass
-        """
+                    def build(self):
+                        pass
+            """)
         self.save_conanfile(conanfile)
         with tools.environment_append({"CONAN_DOCKER_ENTRY_SCRIPT": "pip install -U /tmp/cpt",
                                        "CONAN_USERNAME": "bar",