test/TestCommon.gmk
author tschatzl
Mon, 26 Mar 2018 16:51:43 +0200
changeset 49608 1852b17b0efc
parent 49194 ece10494786c
child 49552 05883543ee2a
permissions -rw-r--r--
8196485: FromCardCache default card index can cause crashes Summary: The default value of -1 for 32 bit card indices is a regular card value at the border of 2TB heap addresses in the from card cache, so G1 may loose remembered set entries. Extend from card cache entries to 64 bits. Reviewed-by: shade, sjohanss Contributed-by: Thomas Schatzl <thomas.schatzl@oracle.com>, Jarkko Miettinen <jarkko.miettinen@relex.fi>

#
# Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.  Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#

#
# Common logic to run various tests for a component, to be included by the
# component specific test makefiles.
#

# Default values for some properties that can be overridden by components.
USE_JTREG_VERSION ?= 4.2
JTREG_VM_TYPE ?= -agentvm
USE_JTREG_ASSERT ?= true
LIMIT_JTREG_VM_MEMORY ?= true

X:=
SPACE:=$(X) $(X)

.DEFAULT : all

# Empty these to get rid of some default rules
.SUFFIXES:
.SUFFIXES: .java
CO=
GET=

# Utilities used
AWK       = awk
CAT       = cat
CD        = cd
CHMOD     = chmod
CP        = cp
CUT       = cut
DIRNAME   = dirname
ECHO      = echo
EGREP     = egrep
EXPAND    = expand
FIND      = find
MKDIR     = mkdir
PWD       = pwd
RM        = rm -f
SED       = sed
SORT      = sort
TEE       = tee
UNAME     = uname
UNIQ      = uniq
WC        = wc
ZIPEXE    = zip

# Get OS name from uname (Cygwin inexplicably adds _NT-5.1)
UNAME_S := $(shell $(UNAME) -s | $(CUT) -f1 -d_)

# Commands to run on paths to make mixed paths for java on windows
ifeq ($(findstring CYGWIN,$(UNAME_S)), CYGWIN)
  # Location of developer shared files
  SLASH_JAVA = J:
  GETMIXEDPATH = cygpath -m
  PLATFORM = windows
else
  # Location of developer shared files
  SLASH_JAVA = /java
  GETMIXEDPATH = $(ECHO)
  PLATFORM = unix # we only care about windows or bsd.
  ifeq ($(UNAME_S), Darwin)
    PLATFORM = bsd
  endif
  ifeq ($(findstring BSD,$(UNAME_S)), BSD)
    PLATFORM = bsd
  endif
endif

# convert list of directories to dos paths
define MixedDirs
$(foreach i,$1,$(shell $(GETMIXEDPATH) "${i}"))
endef

ifdef ALT_SLASH_JAVA
  SLASH_JAVA = $(ALT_SLASH_JAVA)
endif

# Root of this test area (important to use full paths in some places)
TEST_ROOT := $(shell $(PWD))

# Root of all test results
ifdef TEST_OUTPUT_DIR
  $(shell $(MKDIR) -p $(TEST_OUTPUT_DIR)/jtreg)
  ABS_TEST_OUTPUT_DIR := \
    $(shell $(CD) $(TEST_OUTPUT_DIR)/jtreg && $(PWD))
else
  ifdef ALT_OUTPUTDIR
    ABS_OUTPUTDIR = $(shell $(CD) $(ALT_OUTPUTDIR) && $(PWD))
  else
    ABS_OUTPUTDIR = $(shell $(CD) $(TEST_ROOT)/.. && $(PWD))
  endif

  ABS_PLATFORM_BUILD_ROOT = $(ABS_OUTPUTDIR)
  ABS_TEST_OUTPUT_DIR := $(ABS_PLATFORM_BUILD_ROOT)/testoutput/$(UNIQUE_DIR)
endif

# Expect JPRT to set PRODUCT_HOME (the product or jdk in this case to test)
ifndef PRODUCT_HOME
  # Try to use images/jdk if it exists
  ABS_JDK_IMAGE = $(ABS_PLATFORM_BUILD_ROOT)/images/jdk
  PRODUCT_HOME :=                               \
    $(shell                                     \
      if [ -d $(ABS_JDK_IMAGE) ] ; then         \
         $(ECHO) "$(ABS_JDK_IMAGE)";            \
       else                                     \
         $(ECHO) "$(ABS_PLATFORM_BUILD_ROOT)";  \
       fi)
  PRODUCT_HOME := $(PRODUCT_HOME)
endif

# On Windows, setup the _NT_SYMBOL_PATH if possible.
ifeq ($(PLATFORM), windows)
  ifndef _NT_SYMBOL_PATH
    ifdef PRODUCT_SYMBOLS_HOME
      _NT_SYMBOL_PATH := \
          $(subst $(SPACE),;,$(strip $(call MixedDirs, $(sort $(dir $(wildcard \
          $(addprefix $(PRODUCT_SYMBOLS_HOME)/bin/, *.pdb */*.pdb)))))))
      export _NT_SYMBOL_PATH
    endif
  endif
  JTREG_BASIC_OPTIONS += -e:_NT_SYMBOL_PATH='$(_NT_SYMBOL_PATH)'
endif

# Expect JPRT to set JPRT_PRODUCT_ARGS (e.g. -server etc.)
#   Should be passed into 'java' only.
#   Could include: -d64 -server -client OR any java option
ifdef JPRT_PRODUCT_ARGS
  JAVA_ARGS = $(JPRT_PRODUCT_ARGS)
endif

# Expect JPRT to set JPRT_PRODUCT_VM_ARGS (e.g. -Xcomp etc.)
#   Should be passed into anything running the vm (java, javac, javadoc, ...).
ifdef JPRT_PRODUCT_VM_ARGS
  JAVA_VM_ARGS = $(JPRT_PRODUCT_VM_ARGS)
endif

ifneq ($(NATIVE_TEST_PATH), )
  # jtreg -nativepath <dir>
  #
  # Local make tests will be TEST_IMAGE_DIR and JPRT with jprt.use.reg.test.bundle=true
  # should be JPRT_TESTNATIVE_PATH
  ifdef TEST_IMAGE_DIR
    TESTNATIVE_DIR = $(TEST_IMAGE_DIR)
  else ifdef JPRT_TESTNATIVE_PATH
    TESTNATIVE_DIR = $(JPRT_TESTNATIVE_PATH)
  endif
  ifdef TESTNATIVE_DIR
    JTREG_NATIVE_PATH = -nativepath:$(shell $(GETMIXEDPATH) "$(TESTNATIVE_DIR)/$(NATIVE_TEST_PATH)")
  endif
endif

ifeq ($(USE_FAILURE_HANDLER), true)
  # jtreg failure handler config
  ifeq ($(FAILURE_HANDLER_DIR), )
    ifneq ($(TESTNATIVE_DIR), )
      FAILURE_HANDLER_DIR := $(TESTNATIVE_DIR)/failure_handler
    endif
  endif
  ifneq ($(FAILURE_HANDLER_DIR), )
    FAILURE_HANDLER_DIR_MIXED := $(shell $(GETMIXEDPATH) "$(FAILURE_HANDLER_DIR)")
    JTREG_FAILURE_HANDLER_OPTIONS := \
        -timeoutHandlerDir:$(FAILURE_HANDLER_DIR_MIXED)/jtregFailureHandler.jar \
        -observerDir:$(FAILURE_HANDLER_DIR_MIXED)/jtregFailureHandler.jar \
        -timeoutHandler:jdk.test.failurehandler.jtreg.GatherProcessInfoTimeoutHandler \
        -observer:jdk.test.failurehandler.jtreg.GatherDiagnosticInfoObserver \
        -timeoutHandlerTimeout:0
  ifeq ($(PLATFORM), windows)
      JTREG_FAILURE_HANDLER_OPTIONS += -J-Djava.library.path="$(FAILURE_HANDLER_DIR_MIXED)"
    endif
  endif
endif


# Optionally create a CDS archive before running tests
ifeq ($(GENERATE_CDS_ARCHIVE), true)
  CDS_ARCHIVE_FILE := $(ABS_TEST_OUTPUT_DIR)/cds_archive.jsa

  $(CDS_ARCHIVE_FILE): $(PRODUCT_HOME)
	$(PRODUCT_HOME)/bin/java -XX:+UnlockDiagnosticVMOptions \
	    -XX:SharedArchiveFile=$(shell $(GETMIXEDPATH) "$(CDS_ARCHIVE_FILE)") -Xshare:dump

  CDS_VM_ARGS := -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=$(shell $(GETMIXEDPATH) "$(CDS_ARCHIVE_FILE)")
  JTREG_TEST_OPTIONS += $(addprefix -vmoption:, $(CDS_VM_ARGS))
  TEST_PREREQS += $(CDS_ARCHIVE_FILE)
endif

# Expect JPRT to set JPRT_ARCHIVE_BUNDLE (path to zip bundle for results)
ifdef JPRT_ARCHIVE_BUNDLE
  ARCHIVE_BUNDLE = $(JPRT_ARCHIVE_BUNDLE)
endif

# How to create the test bundle (pass or fail, we want to create this)
#   Follow command with ";$(BUNDLE_UP_AND_EXIT)", so it always gets executed.
ifneq ($(ARCHIVE_BUNDLE), )
  ZIP_UP_RESULTS = ( $(MKDIR) -p `$(DIRNAME) $(ARCHIVE_BUNDLE)`     \
	           && $(CD) $(ABS_TEST_OUTPUT_DIR)             \
	           && $(CHMOD) -R a+r . \
	           && $(ZIPEXE) -q -r $(ARCHIVE_BUNDLE) . ) ;
  CLEAN_ARCHIVE_BUNDLE = @$(RM) $(ARCHIVE_BUNDLE)
endif

# AddressSanitizer
ifeq ($(ASAN_ENABLED), yes)
  export ASAN_OPTIONS="handle_segv=0 detect_leaks=0"
  JTREG_BASIC_OPTIONS += -e:ASAN_OPTIONS=$(ASAN_OPTIONS)
  ifneq ($(DEVKIT_LIB_DIR),)
    export LD_LIBRARY_PATH:=$(LD_LIBRARY_PATH):$(DEVKIT_LIB_DIR)
    JTREG_BASIC_OPTIONS += -e:LD_LIBRARY_PATH=$(LD_LIBRARY_PATH)
  endif
endif

# important results files
SUMMARY_TXT = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/JTreport/text/summary.txt")
STATS_TXT_NAME = Stats.txt
STATS_TXT = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/$(STATS_TXT_NAME)")
RUNLIST   = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/runlist.txt")
PASSLIST  = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/passlist.txt")
FAILLIST  = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/faillist.txt")
EXITCODE  = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/exitcode.txt")

TESTEXIT = \
  if [ ! -s $(EXITCODE) ] ; then \
    $(ECHO) "ERROR: EXITCODE file not filled in."; \
    $(ECHO) "1" > $(EXITCODE); \
  fi ; \
  testExitCode=`$(CAT) $(EXITCODE)`; \
  $(ECHO) "EXIT CODE: $${testExitCode}"; \
  exit $${testExitCode}

ifeq ($(TREAT_EXIT_CODE_1_AS_0), true)
  ADJUST_EXIT_CODE := \
  if [ $${jtregExitCode} = 1 ] ; then \
    jtregExitCode=0; \
  fi
else
  # colon is the shell no-op
  ADJUST_EXIT_CODE := :
endif

BUNDLE_UP_AND_EXIT = \
( \
  jtregExitCode=$$? && \
  _summary="$(SUMMARY_TXT)"; \
  $(ADJUST_EXIT_CODE) ; \
  $(RM) -f $(STATS_TXT) $(RUNLIST) $(PASSLIST) $(FAILLIST) $(EXITCODE); \
  $(ECHO) "$${jtregExitCode}" > $(EXITCODE); \
  if [ -r "$${_summary}" ] ; then \
    $(ECHO) "Summary: $(UNIQUE_DIR)" > $(STATS_TXT); \
    $(EXPAND) $${_summary} | $(EGREP) -v ' Not run\.' > $(RUNLIST); \
    $(EGREP) ' Passed\.' $(RUNLIST) \
      | $(EGREP) -v ' Error\.' \
      | $(EGREP) -v ' Failed\.' > $(PASSLIST); \
    ( $(EGREP) ' Failed\.' $(RUNLIST); \
      $(EGREP) ' Error\.' $(RUNLIST); \
      $(EGREP) -v ' Passed\.' $(RUNLIST) ) \
      | $(SORT) | $(UNIQ) > $(FAILLIST); \
    if [ $${jtregExitCode} != 0 -o -s $(FAILLIST) ] ; then \
      $(EXPAND) $(FAILLIST) \
        | $(CUT) -d' ' -f1 \
        | $(SED) -e 's@^@FAILED: @' >> $(STATS_TXT); \
      if [ $${jtregExitCode} = 0 ] ; then \
        jtregExitCode=1; \
      fi; \
    fi; \
    runc="`$(CAT) $(RUNLIST)      | $(WC) -l | $(AWK) '{print $$1;}'`"; \
    passc="`$(CAT) $(PASSLIST)    | $(WC) -l | $(AWK) '{print $$1;}'`"; \
    failc="`$(CAT) $(FAILLIST)    | $(WC) -l | $(AWK) '{print $$1;}'`"; \
    exclc="FIXME CODETOOLS-7900176"; \
    $(ECHO) "TEST STATS: name=$(UNIQUE_DIR)  run=$${runc}  pass=$${passc}  fail=$${failc}" \
      >> $(STATS_TXT); \
  else \
    $(ECHO) "Missing file: $${_summary}" >> $(STATS_TXT); \
  fi; \
  if [ -f $(STATS_TXT) ] ; then \
    $(CAT) $(STATS_TXT); \
  fi; \
  $(ZIP_UP_RESULTS) \
  $(TESTEXIT) \
)

################################################################

# Prep for output
# Change execute permissions on shared library files.
# Files in repositories should never have execute permissions, but
# there are some tests that have pre-built shared libraries, and these
# windows dll files must have execute permission. Adding execute
# permission may happen automatically on windows when using certain
# versions of mercurial but it cannot be guaranteed. And blindly
# adding execute permission might be seen as a mercurial 'change', so
# we avoid adding execute permission to repository files. But testing
# from a plain source tree needs the chmod a+rx. Applying the chmod to
# all shared libraries not just dll files. And with CYGWIN and sshd
# service, you may need CYGWIN=ntsec for this to work.
prep:
	@$(MKDIR) -p $(ABS_TEST_OUTPUT_DIR)
	@if [ ! -d $(TEST_ROOT)/../../.hg ] && [ ! -d $(TEST_ROOT)/../../../.hg ]; then  \
	  $(FIND) $(TEST_ROOT) \( -name \*.dll -o -name \*.DLL -o -name \*.so \)  \
	        -exec $(CHMOD) a+rx {} \; ;                                       \
	fi

ifeq ($(CLEAN_BEFORE_PREP), true)
prep: clean

endif

# Cleanup
clean:
	@$(RM) -r $(ABS_TEST_OUTPUT_DIR)
	$(CLEAN_ARCHIVE_BUNDLE)

################################################################

# jtreg tests

# Expect JT_HOME to be set for jtreg tests. (home for jtreg)
ifndef JT_HOME
  JT_HOME = $(SLASH_JAVA)/re/jtreg/$(USE_JTREG_VERSION)/promoted/latest/binaries/jtreg
  ifdef JPRT_JTREG_HOME
    JT_HOME = $(JPRT_JTREG_HOME)
  endif
endif

# Problematic tests to be excluded
EXTRA_PROBLEM_LISTS :=
PROBLEM_LISTS := ProblemList.txt $(EXTRA_PROBLEM_LISTS)

# Create exclude list for this platform and arch
ifdef NO_EXCLUDES
  JTREG_EXCLUSIONS =
else
  JTREG_EXCLUSIONS = $(addprefix -exclude:, $(wildcard $(PROBLEM_LISTS)))
endif

# ------------------------------------------------------------------

# When called from JPRT the TESTDIRS variable is set to the jtreg tests to run
ifdef TESTDIRS
  TEST_SELECTION = $(TESTDIRS)
endif

ifeq ($(UNAME_S), SunOS)
  NUM_CORES := $(shell LC_MESSAGES=C /usr/sbin/psrinfo -v | grep -c on-line)
endif
ifeq ($(UNAME_S), Linux)
  NUM_CORES := $(shell cat /proc/cpuinfo  | grep -c processor)
endif
ifeq ($(UNAME_S), Darwin)
  NUM_CORES := $(shell /usr/sbin/sysctl -n hw.ncpu)
endif
ifeq ($(findstring CYGWIN,$(UNAME_S)), CYGWIN)
  ifneq ($(NUMBER_OF_PROCESSORS), )
    NUM_CORES := $(NUMBER_OF_PROCESSORS)
  else
    ifneq ($(HOTSPOT_BUILD_JOBS), )
      NUM_CORES := $(HOTSPOT_BUILD_JOBS)
    else
      NUM_CORES := 1 # fallback
    endif
  endif
endif

ifndef CONCURRENCY_FACTOR
  CONCURRENCY_FACTOR = 1
endif

# Concurrency based on min(cores / 2, 12) * CONCURRENCY_FACTOR
CONCURRENCY := $(shell $(AWK) \
  'BEGIN { \
    c = $(NUM_CORES) / 2; \
    if (c > 12) c = 12; \
    c = c * $(CONCURRENCY_FACTOR); \
    if (c < 1) c = 1; \
    printf "%.0f", c; \
  }')
JTREG_BASIC_OPTIONS += -concurrency:$(CONCURRENCY)

# Make sure MaxRAMPercentage is low enough to not cause OOM or swapping since
# we may end up with a lot of JVM's
MAX_RAM_PERCENTAGE := $(shell expr 25 / $(CONCURRENCY))
JTREG_BASIC_OPTIONS += -vmoption:-XX:MaxRAMPercentage=$(MAX_RAM_PERCENTAGE)

ifdef EXTRA_JTREG_OPTIONS
  JTREG_BASIC_OPTIONS += $(EXTRA_JTREG_OPTIONS)
endif

# Default JTREG to run
JTREG = $(JT_HOME)/bin/jtreg
# run in agentvm/othervm mode
JTREG_BASIC_OPTIONS += $(JTREG_VM_TYPE)
# Only run automatic tests
JTREG_BASIC_OPTIONS += -a
# Always turn on assertions
ifeq ($(USE_JTREG_ASSERT), true)
  JTREG_ASSERT_OPTION = -ea -esa
endif
JTREG_BASIC_OPTIONS += $(JTREG_ASSERT_OPTION)
# jtreg verbosity setting
# Default is to report details on all failed or error tests, times too
JTREG_VERBOSE ?= fail,error,time
JTREG_BASIC_OPTIONS += $(if $(JTREG_VERBOSE),-v:$(JTREG_VERBOSE))
# Retain all files for failing tests
JTREG_BASIC_OPTIONS += -retain:fail,error
# Ignore tests are not run and completely silent about it
JTREG_IGNORE_OPTION = -ignore:quiet
JTREG_BASIC_OPTIONS += $(JTREG_IGNORE_OPTION)
# Multiply by 4 the timeout factor
JTREG_TIMEOUT_OPTION =  -timeoutFactor:4
JTREG_BASIC_OPTIONS += $(JTREG_TIMEOUT_OPTION)
ifeq ($(LIMIT_JTREG_VM_MEMORY), true)
  # Set the max memory for jtreg control vm
  JTREG_MEMORY_OPTION = -J-Xmx512m
  JTREG_BASIC_OPTIONS += $(JTREG_MEMORY_OPTION)
  # Set the max memory for jtreg target test vms
  JTREG_TESTVM_MEMORY_OPTION = -vmoption:-Xmx512m
  JTREG_TEST_OPTIONS += $(JTREG_TESTVM_MEMORY_OPTION)
endif
# Make it possible to specify the JIB_DATA_DIR for tests using the
# JIB Artifact resolver
JTREG_BASIC_OPTIONS += -e:JIB_DATA_DIR
# Give tests access to JT_JAVA, see JDK-8141609
JTREG_BASIC_OPTIONS += -e:JDK8_HOME=${JT_JAVA}
# Give aot tests access to Visual Studio installation
ifneq ($(VS120COMNTOOLS), )
  JTREG_BASIC_OPTIONS += -e:VS120COMNTOOLS=$(shell $(GETMIXEDPATH) "$(VS120COMNTOOLS)")
endif
# Set other vm and test options
JTREG_TEST_OPTIONS += $(JAVA_ARGS:%=-javaoptions:%) $(JAVA_VM_ARGS:%=-vmoption:%)
ifneq ($(JIB_JAR), )
  JTREG_BASIC_OPTIONS += -cpa:$(shell $(GETMIXEDPATH) "$(JIB_JAR)")
endif
ifeq ($(IGNORE_MARKED_TESTS), true)
  # Option to tell jtreg to not run tests marked with "ignore"
  ifeq ($(PLATFORM), windows)
    JTREG_KEY_OPTION = -k:!ignore
  else
    JTREG_KEY_OPTION = -k:\!ignore
  endif
  JTREG_BASIC_OPTIONS += $(JTREG_KEY_OPTION)
endif

# Make sure jtreg exists
ifeq ($(USE_WINDOWS_EXISTENCE_CHECK), true)
  jtreg_exists:
	test -d $(shell $(GETMIXEDPATH) "$(JT_HOME)")
	test -f $(shell $(GETMIXEDPATH) "$(JTREG)")

else
  jtreg_exists: $(JT_HOME)
endif
PHONY_LIST += jtreg_exists

# Run jtreg
jtreg_tests: prep jtreg_exists $(PRODUCT_HOME) $(TEST_PREREQS)
	(                                                                    \
	  ( JT_HOME=$(shell $(GETMIXEDPATH) "$(JT_HOME)");                   \
	    export JT_HOME;                                                  \
	    $(shell $(GETMIXEDPATH) "$(JTREG)")                              \
	      $(JTREG_BASIC_OPTIONS)                                         \
	      -r:$(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/JTreport")  \
	      -w:$(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/JTwork")    \
	      -jdk:$(shell $(GETMIXEDPATH) "$(PRODUCT_HOME)")                \
	      $(JTREG_NATIVE_PATH)                                           \
	      $(JTREG_FAILURE_HANDLER_OPTIONS)                               \
	      $(JTREG_EXCLUSIONS)                                            \
	      $(JTREG_TEST_OPTIONS)                                          \
	      $(TEST_SELECTION)                                              \
	  ) ;                                                                \
	  $(BUNDLE_UP_AND_EXIT)                                              \
	) 2>&1 | $(TEE) $(ABS_TEST_OUTPUT_DIR)/output.txt ; $(TESTEXIT)

PHONY_LIST += jtreg_tests

# Make it possible to call this with <component>_jtreg_tests
%_jtreg_tests: jtreg_tests
	# Must have a fake recipe here to prevent make from matching any other rule

################################################################

# Phony targets (e.g. these are not filenames)
.PHONY: all clean prep $(PHONY_LIST)

################################################################