Repository: conan-io/conan

Issue:
CONAN_RUN_TEST: getenv available in recipes for a checker to run unit tests

Discussion:

Patch:
diff --git a/conans/client/build/autotools_environment.py b/conans/client/build/autotools_environment.py
index 6671d87ad..2d99729ba 100644
--- a/conans/client/build/autotools_environment.py
+++ b/conans/client/build/autotools_environment.py
@@ -184,7 +184,10 @@ class AutoToolsBuildEnvironment(object):
 
     @property
     def _sysroot_flag(self):
-        return "--sysroot=%s" % self._adjust_path(self._deps_cpp_info.sysroot) if self._deps_cpp_info.sysroot else None
+        if self._compiler == 'Visual Studio':
+            return None
+        else:
+            return "--sysroot=%s" % self._adjust_path(self._deps_cpp_info.sysroot) if self._deps_cpp_info.sysroot else None
 
     def _configure_link_flags(self):
         """Not the -L"""
@@ -198,7 +201,7 @@ class AutoToolsBuildEnvironment(object):
     def _configure_flags(self):
         ret = copy.copy(self._deps_cpp_info.cflags)
         ret.append(self._architecture_flag)
-        if self._build_type == "Debug":
+        if self._build_type == "Debug" and str(self._compiler) in ['gcc', 'clang', 'apple-clang', 'sun-cc']:
             ret.append("-g")  # default debug information
         elif self._build_type == "Release" and self._compiler == "gcc":
             # Remove all symbol table and relocation information from the executable.
@@ -226,7 +229,9 @@ class AutoToolsBuildEnvironment(object):
 
     @property
     def _architecture_flag(self):
-        return architecture_dict.get(self._arch, "")
+        if str(self._compiler) in ['gcc', 'clang', 'apple-clang', 'sun-cc']:
+            return architecture_dict.get(self._arch, "")
+        return ""
 
     def _get_vars(self):
         def append(*args):
@@ -239,15 +244,21 @@ class AutoToolsBuildEnvironment(object):
                         ret.append(arg)
             return ret
 
-        lib_paths = ['-L%s' % self._adjust_path(x.replace("\\", "/")) for x in self.library_paths]
-        include_paths = ['-I%s' % self._adjust_path(x.replace("\\", "/")) for x in self.include_paths]
+        if self._compiler == 'Visual Studio':
+            lib_paths = ['/LIBPATH:%s' % x.replace("/", "\\") for x in self.library_paths]
+            include_paths = ['-I%s' % x.replace("/", "\\") for x in self.include_paths]
+            libs = [lib for lib in self.libs]
+        else:
+            lib_paths = ['-L%s' % self._adjust_path(x.replace("\\", "/")) for x in self.library_paths]
+            include_paths = ['-I%s' % self._adjust_path(x.replace("\\", "/")) for x in self.include_paths]
+            libs = ['-l%s' % lib for lib in self.libs]
 
         ld_flags = append(self.link_flags, lib_paths)
+
         cpp_flags = append(include_paths, ["-D%s" % x for x in self.defines])
-        libs = ['-l%s' % lib for lib in self.libs]
 
         tmp_compilation_flags = copy.copy(self.flags)
-        if self.fpic:
+        if self.fpic and not self._compiler == 'Visual Studio':
             tmp_compilation_flags.append("-fPIC")
 
         cxx_flags = append(tmp_compilation_flags, self.cxx_flags)
diff --git a/conans/client/build/cmake.py b/conans/client/build/cmake.py
index 86ac0abe4..264fb76fb 100644
--- a/conans/client/build/cmake.py
+++ b/conans/client/build/cmake.py
@@ -25,7 +25,7 @@ def _get_env_cmake_system_name():
 class CMake(object):
 
     def __init__(self, conanfile, generator=None, cmake_system_name=True,
-                 parallel=True, build_type=None, toolset=None):
+                 parallel=True, build_type=None, toolset=None, make_program=None):
         """
         :param settings_or_conanfile: Conanfile instance (or settings for retro compatibility)
         :param generator: Generator name to use or none to autodetect
@@ -65,6 +65,15 @@ class CMake(object):
             # Call the setter to warn and update the definitions if needed
             self.build_type = build_type
 
+        make_program = os.getenv("CONAN_MAKE_PROGRAM") or make_program
+        if make_program:
+            if not tools.which(make_program):
+                self._conanfile.output.warn("The specified make program '%s' cannot be found"
+                                            "and will be ignored" % make_program)
+            else:
+                self._conanfile.output.info("Using '%s' as CMAKE_MAKE_PROGRAM" % make_program)
+                self.definitions["CMAKE_MAKE_PROGRAM"] = make_program
+
     @property
     def build_folder(self):
         return self.build_dir
@@ -318,10 +327,11 @@ class CMake(object):
 
     def configure(self, args=None, defs=None, source_dir=None, build_dir=None,
                   source_folder=None, build_folder=None, cache_build_folder=None):
+
+
         # TODO: Deprecate source_dir and build_dir in favor of xxx_folder
         args = args or []
         defs = defs or {}
-
         source_dir, self.build_dir = self._get_dirs(source_folder, build_folder,
                                                     source_dir, build_dir,
                                                     cache_build_folder)
diff --git a/conans/client/cmd/profile.py b/conans/client/cmd/profile.py
index 4c3ec76aa..ca0a8430f 100644
--- a/conans/client/cmd/profile.py
+++ b/conans/client/cmd/profile.py
@@ -44,7 +44,11 @@ def cmd_profile_create(profile_name, cache_profiles_path, output, detect=False):
 
     contents = profile.dumps()
     save(profile_path, contents)
-    output.info("Empty profile created: %s" % profile_path)
+
+    if detect:
+        output.info("Profile created with detected settings: %s" % profile_path)
+    else:
+        output.info("Empty profile created: %s" % profile_path)
     return profile_path
 
 
diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py
index 0206c410a..552d975ad 100644
--- a/conans/client/conf/__init__.py
+++ b/conans/client/conf/__init__.py
@@ -53,7 +53,7 @@ compiler:
         version: ["4.1", "4.4", "4.5", "4.6", "4.7", "4.8", "4.9",
                   "5", "5.1", "5.2", "5.3", "5.4",
                   "6", "6.1", "6.2", "6.3", "6.4",
-                  "7", "7.1", "7.2"]
+                  "7", "7.1", "7.2", "7.3"]
         libcxx: [libstdc++, libstdc++11]
         threads: [None, posix, win32] #  Windows MinGW
         exception: [None, dwarf2, sjlj, seh] # Windows MinGW
diff --git a/conans/client/conf/detect.py b/conans/client/conf/detect.py
index 0a065dd02..286a87807 100644
--- a/conans/client/conf/detect.py
+++ b/conans/client/conf/detect.py
@@ -3,6 +3,7 @@ import platform
 import re
 from subprocess import Popen, PIPE, STDOUT
 
+from conans.client.output import Color
 from conans.model.version import Version
 from conans.tools import vs_installation_path
 
@@ -186,6 +187,23 @@ def _detect_compiler_version(result, output):
             result.append(("compiler.libcxx", "libc++"))
         elif compiler == "gcc":
             result.append(("compiler.libcxx", "libstdc++"))
+            if Version(version) >= Version("5.1"):
+
+                msg = """
+Conan detected a GCC version > 5 but has adjusted the 'compiler.libcxx' setting to
+'libstdc++' for backwards compatibility.
+Your compiler is likely using the new CXX11 ABI by default (libstdc++11).
+
+If you want Conan to use the new ABI, edit the default profile at:
+
+    ~/.conan/profiles/default
+
+adjusting 'compiler.libcxx=libstdc++11'
+"""
+                output.writeln("\n************************* WARNING: GCC OLD ABI COMPATIBILITY "
+                               "***********************\n %s\n************************************"
+                               "************************************************\n\n\n" % msg,
+                               Color.BRIGHT_RED)
         elif compiler == "cc":
             if platform.system() == "SunOS":
                 result.append(("compiler.libstdcxx", "libstdcxx4"))
diff --git a/conans/client/generators/scons.py b/conans/client/generators/scons.py
index d6cf2f4a2..fae561b10 100644
--- a/conans/client/generators/scons.py
+++ b/conans/client/generators/scons.py
@@ -18,6 +18,7 @@ class SConsGenerator(Generator):
                     '        "CCFLAGS"     : {info.cflags},\n'
                     '        "SHLINKFLAGS" : {info.sharedlinkflags},\n'
                     '        "LINKFLAGS"   : {info.exelinkflags},\n'
+                    '        "VERSION"     : "{info.version}",\n'
                     '    }},\n')
 
         sections = []
diff --git a/conans/client/proxy.py b/conans/client/proxy.py
index d2319d18b..19f114f3e 100644
--- a/conans/client/proxy.py
+++ b/conans/client/proxy.py
@@ -205,16 +205,20 @@ class ConanProxy(object):
 
         if self._remote_name:
             output.info("Not found, retrieving from server '%s' " % self._remote_name)
-            remote = self._registry.remote(self._remote_name)
-            return _retrieve_from_remote(remote)
+            ref_remote = self._registry.remote(self._remote_name)
         else:
             ref_remote = self._registry.get_ref(conan_reference)
             if ref_remote:
                 output.info("Retrieving from predefined remote '%s'" % ref_remote.name)
+
+        if ref_remote:
+            try:
                 return _retrieve_from_remote(ref_remote)
-            else:
-                output.info("Not found in local cache, looking in remotes...")
+            except NotFoundException:
+                raise NotFoundException("%s was not found in remote '%s'" % (str(conan_reference),
+                                                                             ref_remote.name))
 
+        output.info("Not found in local cache, looking in remotes...")
         remotes = self._registry.remotes
         for remote in remotes:
             logger.debug("Trying with remote %s" % remote.name)
diff --git a/conans/client/tools/win.py b/conans/client/tools/win.py
index 4627807bd..99ea22167 100644
--- a/conans/client/tools/win.py
+++ b/conans/client/tools/win.py
@@ -1,4 +1,5 @@
 import glob
+import json
 import os
 import platform
 import re
@@ -68,19 +69,13 @@ def vs_installation_path(version):
 
     if version not in vs_installation_path._cached:
         vs_path = None
-        program_files = os.environ.get("ProgramFiles(x86)", os.environ.get("ProgramFiles"))
-        if program_files:
-            vswhere_path = os.path.join(program_files, "Microsoft Visual Studio", "Installer",
-                                        "vswhere.exe")
-            if os.path.isfile(vswhere_path):
-                version_range = "[%d.0, %d.0)" % (int(version), int(version) + 1)
-                try:
-                    output = subprocess.check_output([vswhere_path, "-version", version_range,
-                                                      "-legacy", "-property", "installationPath"])
-                    vs_path = output.decode().strip()
-                    _global_output.info("vswhere detected VS %s in %s" % (version, vs_path))
-                except (ValueError, subprocess.CalledProcessError, UnicodeDecodeError) as e:
-                    _global_output.error("vswhere error: %s" % str(e))
+        legacy_products = vswhere(legacy=True)
+        all_products = vswhere(products=["*"])
+        products = legacy_products + all_products
+
+        for product in products:
+            if product["installationVersion"].startswith(("%d." % int(version))):
+                vs_path = product["installationPath"]
 
         # Remember to cache result
         vs_installation_path._cached[version] = vs_path
@@ -88,6 +83,78 @@ def vs_installation_path(version):
     return vs_installation_path._cached[version]
 
 
+def vswhere(all_=False, prerelease=False, products=None, requires=None, version="", latest=False,
+            legacy=False, property_="", nologo=True):
+
+    # 'version' option only works if Visual Studio 2017 is installed:
+    # https://github.com/Microsoft/vswhere/issues/91
+
+    products = list() if products is None else products
+    requires = list() if requires is None else requires
+
+    if legacy and (products or requires):
+        raise ConanException("The 'legacy' parameter cannot be specified with either the "
+                             "'products' or 'requires' parameter")
+
+    program_files = os.environ.get("ProgramFiles(x86)", os.environ.get("ProgramFiles"))
+
+    vswhere_path = ""
+
+    if program_files:
+        vswhere_path = os.path.join(program_files, "Microsoft Visual Studio", "Installer",
+                                    "vswhere.exe")
+        if not os.path.isfile(vswhere_path):
+            raise ConanException("Cannot locate 'vswhere'")
+    else:
+        raise ConanException("Cannot locate 'Program Files'/'Program Files (x86)' directory")
+
+    arguments = list()
+    arguments.append(vswhere_path)
+    
+    # Output json format
+    arguments.append("-format")
+    arguments.append("json")
+
+    if all_:
+        arguments.append("-all")
+
+    if prerelease:
+        arguments.append("-prerelease")
+
+    if products:
+        arguments.append("-products")
+        arguments.extend(products)
+
+    if requires:
+        arguments.append("-requires")
+        arguments.extend(requires)
+
+    if len(version) is not 0:
+        arguments.append("-version")
+        arguments.append(version)
+
+    if latest:
+        arguments.append("-latest")
+
+    if legacy:
+        arguments.append("-legacy")
+
+    if len(property_) is not 0:
+        arguments.append("-property")
+        arguments.append(property_)
+
+    if nologo:
+        arguments.append("-nologo")
+
+    try:
+        output = subprocess.check_output(arguments)
+        vswhere_out = output.decode().strip()
+    except (ValueError, subprocess.CalledProcessError, UnicodeDecodeError) as e:
+        raise ConanException("vswhere error: %s" % str(e))
+
+    return json.loads(vswhere_out)
+
+
 def find_windows_10_sdk():
     """finds valid Windows 10 SDK version which can be passed to vcvarsall.bat (vcvars_command)"""
     # uses the same method as VCVarsQueryRegistry.bat
diff --git a/conans/tools.py b/conans/tools.py
index 45d360092..3289293a8 100644
--- a/conans/tools.py
+++ b/conans/tools.py
@@ -5,6 +5,8 @@ import requests
 from conans.client.tools import *
 from conans.client.output import ConanOutput
 # noinspection PyUnresolvedReferences
+from conans.util.env_reader import get_env
+# noinspection PyUnresolvedReferences
 from conans.util.files import (_generic_algorithm_sum, load, save, sha256sum,
                                sha1sum, md5sum, md5, touch, relative_dirs, rmdir, mkdir)
 
diff --git a/conans/util/env_reader.py b/conans/util/env_reader.py
index 8628843c8..cd6f77bb8 100644
--- a/conans/util/env_reader.py
+++ b/conans/util/env_reader.py
@@ -9,8 +9,11 @@
 import os
 
 
-def get_env(env_key, default=None, environment=os.environ):
+def get_env(env_key, default=None, environment=None):
     """Get the env variable associated with env_key"""
+    if environment is None:
+        environment = os.environ
+
     env_var = environment.get(env_key, default)
     if env_var != default:
         if isinstance(default, str):
diff --git a/setup.py b/setup.py
index 66e70aad8..f35ae708b 100644
--- a/setup.py
+++ b/setup.py
@@ -25,6 +25,7 @@ def get_requires(filename):
                 requirements.append(line)
     return requirements
 
+
 project_requirements = get_requires("conans/requirements.txt")
 if platform.system() == "Darwin":
     project_requirements.extend(get_requires("conans/requirements_osx.txt"))
@@ -53,7 +54,7 @@ setup(
     # Versions should comply with PEP440.  For a discussion on single-sourcing
     # the version across setup.py and the project code, see
     # https://packaging.python.org/en/latest/single_source_version.html
-    version=load_version(), # + ".rc1",
+    version=load_version(),  # + ".rc1",
 
     description='Conan C/C++ package manager',
     # long_description="An open source, decentralized package manager, to automate building and sharing of packages",
@@ -71,12 +72,14 @@ setup(
 
     # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
     classifiers=[
-        'Development Status :: 4 - Beta',
+        'Development Status :: 5 - Production/Stable',
         'Intended Audience :: Developers',
         'Topic :: Software Development :: Build Tools',
         'License :: OSI Approved :: MIT License',
         'Programming Language :: Python :: 2',
         'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.6'
     ],
 
     # What does your project relate to?


Test patch:
diff --git a/conans/test/build_helpers/autotools_configure_test.py b/conans/test/build_helpers/autotools_configure_test.py
index a78d8fd30..d4ecf2233 100644
--- a/conans/test/build_helpers/autotools_configure_test.py
+++ b/conans/test/build_helpers/autotools_configure_test.py
@@ -44,6 +44,24 @@ class AutoToolsConfigureTest(unittest.TestCase):
         self.assertEquals(runner.command_called, "make %s -j%s" % (things, cpu_count()))
 
     def test_variables(self):
+        # Visual Studio
+        settings = MockSettings({"build_type": "Release",
+                                 "arch": "x86",
+                                 "compiler": "Visual Studio",
+                                 "compiler.version": "14",
+                                 "compiler.runtime": "MD"})
+        conanfile = MockConanfile(settings)
+        self._set_deps_info(conanfile)
+
+        be = AutoToolsBuildEnvironment(conanfile)
+        expected = {'CFLAGS': 'a_c_flag',
+                    'CPPFLAGS': '-Ipath\\includes -Iother\\include\\path -Donedefinition -Dtwodefinition',
+                    'CXXFLAGS': 'a_c_flag  a_cpp_flag',
+                    'LDFLAGS': 'shared_link_flag exe_link_flag  /LIBPATH:one\\lib\\path',
+                    'LIBS': 'onelib twolib'}
+
+        self.assertEquals(be.vars, expected)
+
         # GCC 32
         settings = MockSettings({"build_type": "Release",
                                  "arch": "x86",
diff --git a/conans/test/build_helpers/cmake_test.py b/conans/test/build_helpers/cmake_test.py
index e76bb4ca1..4c418f83c 100644
--- a/conans/test/build_helpers/cmake_test.py
+++ b/conans/test/build_helpers/cmake_test.py
@@ -1,5 +1,6 @@
 import os
 import shutil
+import stat
 import sys
 import unittest
 import platform
@@ -33,6 +34,33 @@ class CMakeTest(unittest.TestCase):
             cmake = CMake(conan_file)
             self.assertIn('-G "My CMake Generator"', cmake.command_line)
 
+    def cmake_make_program_test(self):
+        settings = Settings.loads(default_settings_yml)
+        settings.os = "Linux"
+        settings.compiler = "gcc"
+        settings.compiler.version = "6.3"
+        settings.arch = "x86"
+        settings.build_type = "Release"
+        conan_file = ConanFileMock()
+        conan_file.settings = settings
+        conan_file.source_folder = os.path.join(self.tempdir, "my_cache_source_folder")
+        conan_file.build_folder = os.path.join(self.tempdir, "my_cache_build_folder")
+
+        # Existing make
+        make_path = os.path.join(self.tempdir, "make")
+        save(make_path, "")
+        st = os.stat(make_path)
+        os.chmod(make_path, st.st_mode | stat.S_IEXEC)
+        with tools.environment_append({"CONAN_MAKE_PROGRAM": make_path}):
+            cmake = CMake(conan_file)
+            self.assertEquals(cmake.definitions["CMAKE_MAKE_PROGRAM"], make_path)
+
+        # Not existing make
+        with tools.environment_append({"CONAN_MAKE_PROGRAM": "fake_path/make"}):
+            cmake = CMake(conan_file)
+            self.assertNotIn("CMAKE_MAKE_PROGRAM", cmake.definitions)
+            self.assertIn("The specified make program 'fake_path/make' cannot be found", conan_file.output)
+
     def folders_test(self):
         def quote_var(var):
             return "'%s'" % var if platform.system() != "Windows" else var
diff --git a/conans/test/command/install_test.py b/conans/test/command/install_test.py
index 3284fe5f3..9cc8437e9 100644
--- a/conans/test/command/install_test.py
+++ b/conans/test/command/install_test.py
@@ -2,14 +2,14 @@ import unittest
 import platform
 import os
 
-from conans.test.utils.tools import TestClient
+from conans.test.utils.tools import TestClient, TestServer
 from conans.model.ref import ConanFileReference, PackageReference
 from conans.paths import CONANFILE, CONANINFO
 from conans.model.info import ConanInfo
 from conans.test.utils.cpp_test_files import cpp_hello_conan_files
 from conans.paths import CONANFILE_TXT
 from conans.client.conf.detect import detected_os
-from conans.util.files import load, mkdir
+from conans.util.files import load, mkdir, rmdir
 
 
 class InstallTest(unittest.TestCase):
@@ -359,7 +359,7 @@ class TestConan(ConanFile):
 
         client.run("install . --build=missing -s os=Windows -s os_build=Windows --install-folder=win_dir")
         self.assertIn("Hello/0.1@lasote/stable from local cache\n",
-                      client.out) # Test "from local cache" output message
+                      client.out)  # Test "from local cache" output message
         client.run("install . --build=missing -s os=Macos -s os_build=Macos --install-folder=os_dir")
         conaninfo = load(os.path.join(client.current_folder, "win_dir/conaninfo.txt"))
         self.assertIn("os=Windows", conaninfo)
@@ -428,3 +428,28 @@ class TestConan(ConanFile):
                            ignore_error=True)
         self.assertTrue(error)
         self.assertIn("Conanfile not found", client.out)
+
+    def install_broken_reference_test(self):
+        client = TestClient(servers={"default": TestServer()},
+                            users={"default": [("lasote", "mypass")]})
+        conanfile = """from conans import ConanFile
+class Pkg(ConanFile):
+    pass
+"""
+        client.save({"conanfile.py": conanfile})
+        client.run("export . Hello/0.1@lasote/stable")
+        client.run("remote add_ref Hello/0.1@lasote/stable default")
+        conan_reference = ConanFileReference.loads("Hello/0.1@lasote/stable")
+        rmdir(os.path.join(client.client_cache.conan(conan_reference)))
+
+        error = client.run("install Hello/0.1@lasote/stable", ignore_error=True)
+        self.assertTrue(error)
+        self.assertIn("ERROR: Hello/0.1@lasote/stable was not found in remote 'default'",
+                      client.out)
+
+        # If it was associated, it has to be desasociated
+        client.run("remote remove_ref Hello/0.1@lasote/stable")
+        error = client.run("install Hello/0.1@lasote/stable", ignore_error=True)
+        self.assertTrue(error)
+        self.assertIn("ERROR: Unable to find 'Hello/0.1@lasote/stable' in remotes",
+                      client.out)
diff --git a/conans/test/generators/scons_test.py b/conans/test/generators/scons_test.py
index 7684c11d8..44ecdebaa 100644
--- a/conans/test/generators/scons_test.py
+++ b/conans/test/generators/scons_test.py
@@ -12,10 +12,12 @@ class SConsGeneratorTest(unittest.TestCase):
         ref = ConanFileReference.loads("MyPkg/0.1@lasote/stables")
         cpp_info = CppInfo("")
         cpp_info.defines = ["MYDEFINE1"]
+        cpp_info.version = "0.1"
         conanfile.deps_cpp_info.update(cpp_info, ref.name)
-        ref = ConanFileReference.loads("MyPkg2/0.1@lasote/stables")
+        ref = ConanFileReference.loads("MyPkg2/3.2.3@lasote/stables")
         cpp_info = CppInfo("")
         cpp_info.defines = ["MYDEFINE2"]
+        cpp_info.version = "3.2.3"
         conanfile.deps_cpp_info.update(cpp_info, ref.name)
         generator = SConsGenerator(conanfile)
         content = generator.content
@@ -23,3 +25,6 @@ class SConsGeneratorTest(unittest.TestCase):
         self.assertIn("        \"CPPDEFINES\"  : [\'MYDEFINE2\', \'MYDEFINE1\'],", scons_lines)
         self.assertIn("        \"CPPDEFINES\"  : [\'MYDEFINE1\'],", scons_lines)
         self.assertIn("        \"CPPDEFINES\"  : [\'MYDEFINE2\'],", scons_lines)
+        self.assertIn("        \"VERSION\"     : \"None\",", scons_lines)
+        self.assertIn("        \"VERSION\"     : \"0.1\",", scons_lines)
+        self.assertIn("        \"VERSION\"     : \"3.2.3\",", scons_lines)
diff --git a/conans/test/util/tools_test.py b/conans/test/util/tools_test.py
index c66818d0f..7c690dc47 100644
--- a/conans/test/util/tools_test.py
+++ b/conans/test/util/tools_test.py
@@ -71,6 +71,85 @@ class ToolsTest(unittest.TestCase):
         with tools.environment_append({"CONAN_CPU_COUNT": "34"}):
             self.assertEquals(tools.cpu_count(), 34)
 
+    def get_env_unit_test(self):
+        """
+        Unit tests tools.get_env
+        """
+        # Test default
+        self.assertIsNone(
+            tools.get_env("NOT_DEFINED", environment={}),
+            None
+        )
+        # Test defined default
+        self.assertEqual(
+            tools.get_env("NOT_DEFINED_KEY", default="random_default", environment={}),
+            "random_default"
+        )
+        # Test return defined string
+        self.assertEqual(
+            tools.get_env("FROM_STR", default="", environment={"FROM_STR": "test_string_value"}),
+            "test_string_value"
+        )
+        # Test boolean conversion
+        self.assertEqual(
+            tools.get_env("BOOL_FROM_STR", default=False, environment={"BOOL_FROM_STR": "1"}),
+            True
+        )
+        self.assertEqual(
+            tools.get_env("BOOL_FROM_STR", default=True, environment={"BOOL_FROM_STR": "0"}),
+            False
+        )
+        self.assertEqual(
+            tools.get_env("BOOL_FROM_STR", default=False, environment={"BOOL_FROM_STR": "True"}),
+            True
+        )
+        self.assertEqual(
+            tools.get_env("BOOL_FROM_STR", default=True, environment={"BOOL_FROM_STR": ""}),
+            False
+        )
+        # Test int conversion
+        self.assertEqual(
+            tools.get_env("TO_INT", default=2, environment={"TO_INT": "1"}),
+            1
+        )
+        # Test float conversion
+        self.assertEqual(
+            tools.get_env("TO_FLOAT", default=2.0, environment={"TO_FLOAT": "1"}),
+            1.0
+        ),
+        # Test list conversion
+        self.assertEqual(
+            tools.get_env("TO_LIST", default=[], environment={"TO_LIST": "1,2,3"}),
+            ["1", "2", "3"]
+        )
+        self.assertEqual(
+            tools.get_env("TO_LIST_NOT_TRIMMED", default=[], environment={"TO_LIST_NOT_TRIMMED": " 1 , 2 , 3 "}),
+            [" 1 ", " 2 ", " 3 "]
+        )
+
+    def test_get_env_in_conanfile(self):
+        """
+        Test get_env is available and working in conanfile
+        """
+        client = TestClient()
+
+        conanfile = """from conans import ConanFile, tools
+
+class HelloConan(ConanFile):
+    name = "Hello"
+    version = "0.1"
+
+    def build(self):
+        run_tests = tools.get_env("CONAN_RUN_TESTS", default=False)
+        print("test_get_env_in_conafile CONAN_RUN_TESTS=%r" % run_tests)
+        assert(run_tests == True)
+        """
+        client.save({"conanfile.py": conanfile})
+
+        with tools.environment_append({"CONAN_RUN_TESTS": "1"}):
+            client.run("install .")
+            client.run("build .")
+
     def test_global_tools_overrided(self):
         client = TestClient()