def ExportCpeReport(input_proto, output_proto, config): """Export a CPE report. Args: input_proto (BundleRequest): The input proto. output_proto (BundleResponse): The output proto. config (api_config.ApiConfig): The API call config. """ chroot = controller_util.ParseChroot(input_proto.chroot) output_dir = input_proto.output_dir if input_proto.build_target.name: # Legacy handling - use the default sysroot path for the build target. build_target = controller_util.ParseBuildTarget(input_proto.build_target) sysroot = sysroot_lib.Sysroot(build_target.root) elif input_proto.sysroot.path: sysroot = sysroot_lib.Sysroot(input_proto.sysroot.path) else: # TODO(saklein): Switch to validate decorators once legacy handling can be # cleaned up. cros_build_lib.Die('sysroot.path is required.') if config.validate_only: return controller.RETURN_CODE_VALID_INPUT cpe_result = artifacts.GenerateCpeReport(chroot, sysroot, output_dir) output_proto.artifacts.add().path = cpe_result.report output_proto.artifacts.add().path = cpe_result.warnings
def setUp(self): """Setup the test environment.""" # Fake being root to avoid running all filesystem commands with sudo_run. self.PatchObject(os, 'getuid', return_value=0) self.PatchObject(os, 'geteuid', return_value=0) sysroot_path = os.path.join(self.tempdir, 'sysroot') osutils.SafeMakedirs(sysroot_path) self.sysroot = sysroot_lib.Sysroot(sysroot_path) self.relative_sysroot = sysroot_lib.Sysroot('sysroot')
def setUp(self): self.output_dir = os.path.join(self.tempdir, 'output_dir') self.archive_dir = os.path.join(self.tempdir, 'archive_base_dir') sysroot_path = os.path.join(self.tempdir, 'sysroot') self.chroot = chroot_lib.Chroot(self.tempdir) self.sysroot = sysroot_lib.Sysroot('sysroot') self.sysroot_dne = sysroot_lib.Sysroot('sysroot_DNE') # Make sure we have the valid paths. osutils.SafeMakedirs(self.output_dir) osutils.SafeMakedirs(sysroot_path)
def _arch(self): if self._cached_arch is None: self._cached_arch = sysroot_lib.Sysroot( self._sysroot).GetStandardField( sysroot_lib.STANDARD_FIELD_ARCH) return self._cached_arch
def GenerateSysroot(sysroot_path, board, build_tests, unpack_only=False): """Create a sysroot using only binary packages from local binhost. Args: sysroot_path: Where we want to place the sysroot. board: Board we want to build for. build_tests: If we should include autotest packages. unpack_only: If we only want to unpack the binary packages, and not build them. """ osutils.SafeMakedirs(sysroot_path) if not unpack_only: # Generate the sysroot configuration. sysroot = sysroot_lib.Sysroot(sysroot_path) sysroot.WriteConfig(sysroot.GenerateBoardConfiguration(board)) cros_build_lib.RunCommand( [os.path.join(constants.CROSUTILS_DIR, 'install_toolchain'), '--noconfigure', '--sysroot', sysroot_path]) cmd = list(_BUILD_PKGS_CMD) cmd.extend(['--board_root', sysroot_path, '--board', board]) if unpack_only: cmd.append('--unpackonly') if not build_tests: cmd.append('--nowithautotest') env = {'USE': os.environ.get('USE', ''), 'PORTAGE_BINHOST': 'file://%s' % portage_util.GetBinaryPackageDir( sysroot=cros_build_lib.GetSysroot(board))} cros_build_lib.RunCommand(cmd, extra_env=env)
def BundleFirmware(input_proto, output_proto, _config): """Tar the firmware images for a build target. Args: input_proto (BundleRequest): The input proto. output_proto (BundleResponse): The output proto. _config (api_config.ApiConfig): The API call config. """ output_dir = input_proto.output_dir chroot = controller_util.ParseChroot(input_proto.chroot) sysroot_path = input_proto.sysroot.path sysroot = sysroot_lib.Sysroot(sysroot_path) if not chroot.exists(): cros_build_lib.Die('Chroot does not exist: %s', chroot.path) elif not sysroot.Exists(chroot=chroot): cros_build_lib.Die('Sysroot does not exist: %s', chroot.full_path(sysroot.path)) archive = artifacts.BuildFirmwareArchive(chroot, sysroot, output_dir) if archive is None: cros_build_lib.Die( 'Could not create firmware archive. No firmware found for %s.', sysroot_path) output_proto.artifacts.add().path = archive
def InstallToolchain(input_proto, output_proto, _config): """Install the toolchain into a sysroot.""" compile_source = (input_proto.flags.compile_source or input_proto.flags.toolchain_changed) sysroot_path = input_proto.sysroot.path build_target = controller_util.ParseBuildTarget( input_proto.sysroot.build_target) target_sysroot = sysroot_lib.Sysroot(sysroot_path) run_configs = sysroot.SetupBoardRunConfig(usepkg=not compile_source) _LogBinhost(build_target.name) try: sysroot.InstallToolchain(build_target, target_sysroot, run_configs) except sysroot_lib.ToolchainInstallError as e: # Error installing - populate the failed package info. for package in e.failed_toolchain_info: package_info = output_proto.failed_packages.add() controller_util.CPVToPackageInfo(package, package_info) return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE return controller.RETURN_CODE_SUCCESS
def BundleEbuildLogs(input_proto, output_proto, config): """Tar the ebuild logs for a build target. Args: input_proto (BundleRequest): The input proto. output_proto (BundleResponse): The output proto. config (api_config.ApiConfig): The API call config. """ output_dir = input_proto.output_dir sysroot_path = input_proto.sysroot.path chroot = controller_util.ParseChroot(input_proto.chroot) # TODO(mmortensen) Cleanup legacy handling after it has been switched over. target = input_proto.build_target.name if target: # Legacy handling. build_root = constants.SOURCE_ROOT chroot = chroot_lib.Chroot(path=os.path.join(build_root, 'chroot')) sysroot_path = os.path.join('/build', target) # TODO(saklein): Switch to validation_complete decorator after legacy # handling has been cleaned up. if config.validate_only: return controller.RETURN_CODE_VALID_INPUT sysroot = sysroot_lib.Sysroot(sysroot_path) archive = artifacts.BundleEBuildLogsTarball(chroot, sysroot, output_dir) if archive is None: cros_build_lib.Die( 'Could not create ebuild logs archive. No logs found for %s.', sysroot.path) output_proto.artifacts.add().path = os.path.join(output_dir, archive)
def BundleChromeOSConfig(input_proto, output_proto, _config): """Output the ChromeOS Config payload for a build target. Args: input_proto (BundleRequest): The input proto. output_proto (BundleResponse): The output proto. _config (api_config.ApiConfig): The API call config. """ output_dir = input_proto.output_dir sysroot_path = input_proto.sysroot.path chroot = controller_util.ParseChroot(input_proto.chroot) # TODO(mmortensen) Cleanup legacy handling after it has been switched over. target = input_proto.build_target.name if target: # Legacy handling. build_root = constants.SOURCE_ROOT chroot = chroot_lib.Chroot(path=os.path.join(build_root, 'chroot')) sysroot_path = os.path.join('/build', target) sysroot = sysroot_lib.Sysroot(sysroot_path) chromeos_config = artifacts.BundleChromeOSConfig(chroot, sysroot, output_dir) if chromeos_config is None: cros_build_lib.Die( 'Could not create ChromeOS Config payload. No config found for %s.', sysroot.path) output_proto.artifacts.add().path = os.path.join(output_dir, chromeos_config)
def setUp(self): # Create the chroot and sysroot instances. self.chroot_path = os.path.join(self.tempdir, 'chroot_dir') self.chroot = chroot_lib.Chroot(path=self.chroot_path) self.sysroot_path = os.path.join(self.chroot_path, 'sysroot_dir') self.sysroot = sysroot_lib.Sysroot(self.sysroot_path) # Create the output directory. self.output_dir = os.path.join(self.tempdir, 'output_dir') osutils.SafeMakedirs(self.output_dir) # The sysroot's /var/db/pkg prefix for the chrome package directories. var_db_pkg = os.path.join(self.sysroot_path, 'var', 'db', 'pkg') # Create the var/db/pkg dir so we have that much for no-chrome tests. osutils.SafeMakedirs(var_db_pkg) # Two versions of chrome to test the multiple version checks/handling. chrome_v1 = '%s-1.0.0-r1' % constants.CHROME_PN chrome_v2 = '%s-2.0.0-r1' % constants.CHROME_PN # Build the two chrome version paths. chrome_cat_dir = os.path.join(var_db_pkg, constants.CHROME_CN) self.chrome_v1_dir = os.path.join(chrome_cat_dir, chrome_v1) self.chrome_v2_dir = os.path.join(chrome_cat_dir, chrome_v2) # Directory tuple for verifying the result archive contents. self.expected_archive_contents = cros_test_lib.Directory( './', 'environment') # Create a environment.bz2 file to put into folders. env_file = os.path.join(self.tempdir, 'environment') osutils.Touch(env_file) cros_build_lib.run(['bzip2', env_file]) self.env_bz2 = '%s.bz2' % env_file
def BundleSimpleChromeArtifacts(input_proto, output_proto, _config): """Create the simple chrome artifacts.""" # Required args. sysroot_path = input_proto.sysroot.path build_target_name = input_proto.sysroot.build_target.name output_dir = input_proto.output_dir # Optional args. chroot_path = input_proto.chroot.path or constants.DEFAULT_CHROOT_PATH cache_dir = input_proto.chroot.cache_dir # Build out the argument instances. build_target = build_target_util.BuildTarget(build_target_name) chroot = chroot_lib.Chroot(path=chroot_path, cache_dir=cache_dir) # Sysroot.path needs to be the fully qualified path, including the chroot. full_sysroot_path = os.path.join(chroot.path, sysroot_path.lstrip(os.sep)) sysroot = sysroot_lib.Sysroot(full_sysroot_path) # Quick sanity check that the sysroot exists before we go on. if not sysroot.Exists(): cros_build_lib.Die('The sysroot does not exist.') try: results = artifacts.BundleSimpleChromeArtifacts(chroot, sysroot, build_target, output_dir) except artifacts.Error as e: cros_build_lib.Die('Error %s raised in BundleSimpleChromeArtifacts: %s', type(e), e) for file_name in results: output_proto.artifacts.add().path = file_name
def BundleSimpleChromeArtifacts(input_proto, output_proto, _config): """Create the simple chrome artifacts.""" sysroot_path = input_proto.sysroot.path output_dir = input_proto.output_dir # Build out the argument instances. build_target = controller_util.ParseBuildTarget( input_proto.sysroot.build_target) chroot = controller_util.ParseChroot(input_proto.chroot) # Sysroot.path needs to be the fully qualified path, including the chroot. full_sysroot_path = os.path.join(chroot.path, sysroot_path.lstrip(os.sep)) sysroot = sysroot_lib.Sysroot(full_sysroot_path) # Quick sanity check that the sysroot exists before we go on. if not sysroot.Exists(): cros_build_lib.Die('The sysroot does not exist.') try: results = artifacts.BundleSimpleChromeArtifacts( chroot, sysroot, build_target, output_dir) except artifacts.Error as e: cros_build_lib.Die( 'Error %s raised in BundleSimpleChromeArtifacts: %s', type(e), e) for file_name in results: output_proto.artifacts.add().path = file_name
def BundleFpmcuUnittests(input_proto, output_proto, _config): """Tar the fingerprint MCU unittest binaries for a build target. Args: input_proto (BundleRequest): The input proto. output_proto (BundleResponse): The output proto. _config (api_config.ApiConfig): The API call config. """ output_dir = input_proto.output_dir chroot = controller_util.ParseChroot(input_proto.chroot) sysroot_path = input_proto.sysroot.path sysroot = sysroot_lib.Sysroot(sysroot_path) if not chroot.exists(): cros_build_lib.Die('Chroot does not exist: %s', chroot.path) elif not sysroot.Exists(chroot=chroot): cros_build_lib.Die('Sysroot does not exist: %s', chroot.full_path(sysroot.path)) archive = artifacts.BundleFpmcuUnittests(chroot, sysroot, output_dir) if archive is None: logging.warning('No fpmcu unittests found for %s.', sysroot_path) return output_proto.artifacts.add().path = archive
def DebugInfoTest(input_proto, _output_proto, config): """Run the debug info tests.""" sysroot_path = input_proto.sysroot.path target_name = input_proto.sysroot.build_target.name if not sysroot_path: if target_name: sysroot_path = cros_build_lib.GetSysroot(target_name) else: cros_build_lib.Die( "The sysroot path or the sysroot's build target name " 'must be provided.') # We could get away with out this, but it's a cheap check. sysroot = sysroot_lib.Sysroot(sysroot_path) if not sysroot.Exists(): cros_build_lib.Die('The provided sysroot does not exist.') if config.validate_only: return controller.RETURN_CODE_VALID_INPUT if test.DebugInfoTest(sysroot_path): return controller.RETURN_CODE_SUCCESS else: return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
def main(argv): opts = ParseArgs(argv) if not cros_build_lib.IsInsideChroot(): raise commandline.ChrootRequiredError() if os.geteuid() != 0: cros_build_lib.SudoRunCommand(sys.argv, print_cmd=False) return output = sys.stdout if opts.out_file: output = open(opts.out_file, 'w') sysroot = sysroot_lib.Sysroot(opts.sysroot) if opts.command == 'create-wrappers': sysroot.CreateAllWrappers(opts.friendlyname) elif opts.command == 'generate-config': config = sysroot.GenerateBoardConfig(opts.board) output.write('\n' + config) elif opts.command == 'generate-make-conf': output.write('\n' + sysroot.GenerateMakeConf(opts.accepted_licenses)) elif opts.command == 'generate-binhosts': output.write( '\n' + sysroot.GenerateBinhostConf(opts.chrome_only, opts.local_only))
def _InstallToolchain(self): # Create the sysroot's config. sysroot = sysroot_lib.Sysroot(self.sysroot) sysroot.WriteConfig(sysroot.GenerateBoardConfig(self.options.board)) cros_build_lib.RunCommand([ os.path.join(constants.CROSUTILS_DIR, 'install_toolchain'), '--noconfigure', '--sysroot', self.sysroot ])
def setUp(self): """Setup the test environment.""" # Fake being root to avoid running commands with sudo_run. self.PatchObject(os, 'getuid', return_value=0) self.PatchObject(os, 'geteuid', return_value=0) self.sysroot = sysroot_lib.Sysroot(self.tempdir) self.emerge = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
def setUp(self): self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True) # A board we have a sysroot for already. self.board = 'board' self.sysroot_path = os.path.join(self.tempdir, 'build', self.board) self.build_target = build_target_util.BuildTarget( self.board, build_root=self.sysroot_path) self.sysroot = sysroot_lib.Sysroot(self.sysroot_path) # A board we don't have a sysroot for yet. self.unbuilt_board = 'board2' self.unbuilt_path = os.path.join(self.tempdir, 'build', self.unbuilt_board) self.unbuilt_target = build_target_util.BuildTarget( self.unbuilt_board, build_root=self.unbuilt_path) self.unbuilt_sysroot = sysroot_lib.Sysroot(self.unbuilt_path) osutils.SafeMakedirs(self.sysroot_path)
def setUp(self): self.chroot = chroot_lib.Chroot(self.tempdir) self.sysroot = sysroot_lib.Sysroot('/sysroot') sysroot_path = os.path.join(self.tempdir, 'sysroot') osutils.SafeMakedirs(sysroot_path) self.pin_dir = os.path.join(sysroot_path, constants.GUEST_IMAGES_PINS_PATH) osutils.SafeMakedirs(self.pin_dir)
def BuildTargetUnitTest(input_proto, output_proto, _config): """Run a build target's ebuild unit tests.""" # Required args. result_path = input_proto.result_path # Method flags. # An empty sysroot means build packages was not run. This is used for # certain boards that need to use prebuilts (e.g. grunt's unittest-only). was_built = not input_proto.flags.empty_sysroot # Packages to be tested. packages_package_info = input_proto.packages packages = [] for package_info_msg in packages_package_info: packages.append(controller_util.PackageInfoToString(package_info_msg)) # Skipped tests. # TODO: Remove blacklist when we fully switch to blocklist. blocklisted_package_info = (input_proto.package_blacklist or input_proto.package_blocklist) blocklist = [] for package_info_msg in blocklisted_package_info: blocklist.append(controller_util.PackageInfoToString(package_info_msg)) # Allow call to succeed if no tests were found. testable_packages_optional = input_proto.flags.testable_packages_optional build_target = controller_util.ParseBuildTarget(input_proto.build_target) chroot = controller_util.ParseChroot(input_proto.chroot) code_coverage = input_proto.flags.code_coverage result = test.BuildTargetUnitTest( build_target, chroot, packages=packages, blocklist=blocklist, was_built=was_built, code_coverage=code_coverage, testable_packages_optional=testable_packages_optional) if not result.success: # Failed to run tests or some tests failed. # Record all failed packages. for cpv in result.failed_cpvs: package_info_msg = output_proto.failed_packages.add() controller_util.CPVToPackageInfo(cpv, package_info_msg) if result.failed_cpvs: return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE else: return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY sysroot = sysroot_lib.Sysroot(build_target.root) tarball = test.BuildTargetUnitTestTarball(chroot, sysroot, result_path) if tarball: output_proto.tarball_path = tarball deserialize_metrics_log(output_proto.events, prefix=build_target.name)
def setUp(self): self.board = 'samus' # Create chroot object and sysroot object chroot_path = os.path.join(self.tempdir, 'chroot') self.chroot = chroot_lib.Chroot(path=chroot_path) sysroot_path = os.path.join('build', self.board) self.sysroot = sysroot_lib.Sysroot(sysroot_path) self.archive_dir = self.tempdir
def setUp(self): self.chroot = chroot_lib.Chroot( path=os.path.join(self.tempdir, 'chroot/path')) self.sysroot = sysroot_lib.Sysroot('/sysroot/path') test_dir = os.path.join( self.chroot.full_path(self.sysroot.path, constants.UNITTEST_PKG_PATH)) osutils.SafeMakedirs(test_dir) self.result_path = os.path.join(self.tempdir, 'result')
def Emerge(packages, sysroot, with_deps=True, rebuild_deps=True, use_binary=True, jobs=None, debug_output=False): """Emerge the specified |packages|. Args: packages: List of packages to emerge. sysroot: Path to the sysroot in which to emerge. with_deps: Whether to include dependencies. rebuild_deps: Whether to rebuild dependencies. use_binary: Whether to use binary packages. jobs: Number of jobs to run in parallel. debug_output: Emit debug level output. Raises: cros_build_lib.RunCommandError: If emerge returns an error. """ cros_build_lib.AssertInsideChroot() if not packages: raise ValueError('No packages provided') cmd = GetEmergeCommand(sysroot) cmd.append('-uNv') modified_packages = workon.ListModifiedWorkonPackages( sysroot_lib.Sysroot(sysroot)) if modified_packages: mod_pkg_list = ' '.join(modified_packages) cmd += [ '--reinstall-atoms=' + mod_pkg_list, '--usepkg-exclude=' + mod_pkg_list ] cmd.append('--deep' if with_deps else '--nodeps') if use_binary: cmd += ['-g', '--with-bdeps=y'] if sysroot == '/': # Only update toolchains in the chroot when binpkgs are available. The # toolchain rollout process only takes place when the chromiumos sdk # builder finishes a successful build and pushes out binpkgs. cmd += ['--useoldpkg-atoms=%s' % ' '.join(_GetToolchainPackages())] if rebuild_deps: cmd.append('--rebuild-if-unbuilt') if jobs: cmd.append('--jobs=%d' % jobs) if debug_output: cmd.append('--show-output') cros_build_lib.SudoRunCommand(cmd + packages)
def testGenerateConfigNoToolchainRaisesError(self): """Tests _GenerateConfig() with no toolchain raises an error.""" self.PatchObject(toolchain, 'FilterToolchains', autospec=True, return_value={}) sysroot = sysroot_lib.Sysroot(self.tempdir) with self.assertRaises(sysroot_lib.ConfigurationError): # pylint: disable=protected-access sysroot._GenerateConfig({}, ['foo_overlay'], ['foo_overlay'], '')
def setUp(self): self.PatchObject(constants, 'SOURCE_ROOT', new=self.tempdir) self.chroot_path = os.path.join(self.tempdir, 'chroot') self.sysroot_path = '/build/foo' self.root = os.path.join(self.chroot_path, self.sysroot_path.lstrip('/'), 'packages') self.chroot = chroot_lib.Chroot(self.chroot_path) self.sysroot = sysroot_lib.Sysroot(self.sysroot_path) self.build_target = build_target_lib.BuildTarget('foo') osutils.SafeMakedirs(self.root)
def PrepareBinhostUploads(input_proto, output_proto, config): """Return a list of files to upload to the binhost. See BinhostService documentation in api/proto/binhost.proto. Args: input_proto (PrepareBinhostUploadsRequest): The input proto. output_proto (PrepareBinhostUploadsResponse): The output proto. config (api_config.ApiConfig): The API call config. """ if input_proto.sysroot.build_target.name: build_target_msg = input_proto.sysroot.build_target else: build_target_msg = input_proto.build_target sysroot_path = input_proto.sysroot.path if not sysroot_path and not build_target_msg.name: cros_build_lib.Die('Sysroot.path is required.') build_target = controller_util.ParseBuildTarget(build_target_msg) chroot = controller_util.ParseChroot(input_proto.chroot) if not sysroot_path: sysroot_path = build_target.root sysroot = sysroot_lib.Sysroot(sysroot_path) uri = input_proto.uri # For now, we enforce that all input URIs are Google Storage buckets. if not gs.PathIsGs(uri): raise ValueError('Upload URI %s must be Google Storage.' % uri) if config.validate_only: return controller.RETURN_CODE_VALID_INPUT parsed_uri = urllib.parse.urlparse(uri) upload_uri = gs.GetGsURL(parsed_uri.netloc, for_gsutil=True).rstrip('/') upload_path = parsed_uri.path.lstrip('/') # Read all packages and update the index. The index must be uploaded to the # binhost for Portage to use it, so include it in upload_targets. uploads_dir = binhost.GetPrebuiltsRoot(chroot, sysroot, build_target) index_path = binhost.UpdatePackageIndex(uploads_dir, upload_uri, upload_path, sudo=True) upload_targets = binhost.GetPrebuiltsFiles(uploads_dir) assert index_path.startswith(uploads_dir), ( 'expected index_path to start with uploads_dir') upload_targets.append(index_path[len(uploads_dir):]) output_proto.uploads_dir = uploads_dir for upload_target in upload_targets: output_proto.upload_targets.add().path = upload_target.strip('/')
def _ParseArgs(argv): """Parse and validate arguments.""" parser = GetParser() opts = parser.parse_args(argv) # Expand the sysroot path to a sysroot object. opts.sysroot = sysroot_lib.Sysroot(opts.sysroot) # Make sure the toolchain value reflects the toolchain we will be using. opts.toolchain = _GetToolchain(opts.toolchain, opts.sysroot) opts.Freeze() return opts
def _overlays(self): """Returns overlays installed for the selected system.""" if self._cached_overlays is None: sysroot = sysroot_lib.Sysroot(self._sysroot) portdir_overlay = sysroot.GetStandardField('PORTDIR_OVERLAY') if portdir_overlay: self._cached_overlays = portdir_overlay.strip().splitlines() else: # This command is exceptionally slow, and we don't expect the list of # overlays to change during the lifetime of WorkonHelper. self._cached_overlays = portage_util.FindSysrootOverlays(self._sysroot) return self._cached_overlays
def main(argv): logging.getLogger().setLevel(logging.INFO) flags = _ParseArguments(argv) sysroot = None if flags.board: sysroot = cros_build_lib.GetSysroot(flags.board) elif flags.host: sysroot = '/' else: sysroot = flags.sysroot modified = ListModifiedWorkonPackages(sysroot_lib.Sysroot(sysroot)) print(' '.join(sorted(modified)))
def testGetStandardField(self): """Tests that standard field can be fetched correctly.""" sysroot = sysroot_lib.Sysroot(self.tempdir) sysroot.WriteConfig('FOO="bar"') self.assertEqual('bar', sysroot.GetStandardField('FOO')) # Works with multiline strings multiline = """foo bar baz """ sysroot.WriteConfig('TEST="%s"' % multiline) self.assertEqual(multiline, sysroot.GetStandardField('TEST'))