diff --git a/Interfaces/API/NewInterface/Applications/KKMC.py b/Interfaces/API/NewInterface/Applications/KKMC.py
new file mode 100644
index 0000000000000000000000000000000000000000..daeb1c4842f81a1084b934c3a09d2672be3dc148
--- /dev/null
+++ b/Interfaces/API/NewInterface/Applications/KKMC.py
@@ -0,0 +1,162 @@
+"""
+KKMC: Interface to the KKMCee generator
+
+.. versionadded:: v29r2p5
+
+Usage:
+
+>>> kkmc = KKMC ()
+>>> kkmc.setVersion('LCG_97a_FCC_4')
+>>> kkmc.setConfigFile( '__path_to__/process.input' )
+
+Usage:
+>>> kkmc = KKMC ()
+>>> kkmc.setVersion('LCG_97a_FCC_4')
+>>> kkmc.setEvtType('Mu')
+>>> kkmc.setEnergy(91.2)
+>>> kkmc.setNumberOfEvents(1000)
+>>> kkmc.setOutputFile('kkmu_1000.LHE')
+
+"""
+
+import types
+import os
+import re
+
+from ILCDIRAC.Interfaces.API.NewInterface.LCApplication import LCApplication
+from DIRAC import S_OK, S_ERROR, gLogger
+from DIRAC.Core.Workflow.Parameter import Parameter
+from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
+
+
+LOG = gLogger.getSubLogger(__name__)
+
+__RCSID__ = "$Id$"
+
+
+class KKMC(LCApplication):
+  """ KKMC Application Class """
+
+  def __init__(self, paramdict=None):
+    self.randomSeed = -1
+    self.eventType = ''
+    self.kkmcConfigFile = ''
+    self.seedFile = ''
+    super(KKMC, self).__init__(paramdict)
+    # Those 5 need to come after default constructor
+    self._modulename = 'KKMCAnalysis'
+    self._moduledescription = 'Module to run KKMC'
+    self.appname = 'kkmc'
+    self.datatype = 'GEN'
+    self._ops = Operations()
+
+  def setEvtType(self, evttype):
+    """ Optional: Flavour to be generated (Mu|Tau|UDS|C|B|Hadrons).
+
+    :param str evttype: Flavour to be generated
+    """
+    self._checkArgs({'evttype': types.StringTypes})
+    if self.addedtojob:
+      return self._reportError("Cannot modify this attribute once application has been added to Job")
+    self.eventType = evttype
+    return S_OK()
+
+  def setConfigFile(self, kkmcConfigFilePath):
+    """ Set the KKMC options to be used
+
+    :param str kkmcConfigFilePath: Path to the KKMC input file.
+    """
+    self._checkArgs({'kkmcConfigFilePath': types.StringType})
+
+    # Chech if file exist
+    if not os.path.isfile(kkmcConfigFilePath):
+      return self._reportError('KKMC config file does not exist!')
+
+    # Read file
+    self.kkmcConfigFile = open(kkmcConfigFilePath).read()
+
+    return None
+
+  def setSeedFile(self, seedFilePath):
+    """ Optional: set the file to be used for seeding (randomly generated, if missing)
+
+    :param str seedFilePath: Path to the KKMC seed file.
+    """
+    self._checkArgs({'seedFilePath': types.StringType})
+
+    # Chech if file exist
+    if not os.path.isfile(seedFilePath):
+      return self._reportError('The seed file does not exist! %s' % seedFilePath)
+
+    # Read file
+    self.seedFile = open(seedFilePath).read()
+
+    return None
+
+  def _userjobmodules(self, stepdefinition):
+    res1 = self._setApplicationModuleAndParameters(stepdefinition)
+    res2 = self._setUserJobFinalization(stepdefinition)
+    if not res1["OK"] or not res2["OK"]:
+      return S_ERROR('userjobmodules failed')
+    return S_OK()
+
+  def _prodjobmodules(self, stepdefinition):
+    res1 = self._setApplicationModuleAndParameters(stepdefinition)
+    res2 = self._setOutputComputeDataList(stepdefinition)
+    if not res1["OK"] or not res2["OK"]:
+      return S_ERROR('prodjobmodules failed')
+    return S_OK()
+
+  def _checkConsistency(self, job=None):
+    """
+    Check consistency of the KKMC application, this is called from the `Job` instance
+
+    :param job: The instance of the job
+    :type job: ~ILCDIRAC.Interfaces.API.NewInterface.Job.Job
+    :returns: S_OK/S_ERROR
+    """
+
+    if not self.version:
+      return S_ERROR('No version found!')
+
+    if self.kkmcConfigFile:
+      return S_OK()
+
+    if not self.eventType and not self.energy and not self.numberOfEvents and not self.outputFile:
+      return S_ERROR('No config file set!')
+    if not self.eventType:
+      return S_ERROR('No event type set!')
+    if not self.energy:
+      return S_ERROR('No energy set!')
+    if not self.numberOfEvents:
+      return S_ERROR('No number of events set!')
+    if not self.outputFile:
+      return S_ERROR('No output file set!')
+
+    return S_OK()
+
+  def _applicationModule(self):
+    md1 = self._createModuleDefinition()
+    md1.addParameter(Parameter("debug", False, "bool", "", "", False, False, "debug mode"))
+    md1.addParameter(Parameter("kkmcConfigFile", '', "string", "", "", False, False, "KKMC steering options"))
+    md1.addParameter(Parameter("seedFile", '', "string", "", "", False, False, "Seed file for the generator"))
+    md1.addParameter(
+        Parameter(
+            "eventType",
+            '',
+            "string",
+            "",
+            "",
+            False,
+            False,
+            "Flavour to be generated (Mu|Tau|UDS|C|B|Hadrons)"))
+    return md1
+
+  def _applicationModuleValues(self, moduleinstance):
+    moduleinstance.setValue("debug", self.debug)
+    moduleinstance.setValue("kkmcConfigFile", self.kkmcConfigFile)
+    moduleinstance.setValue("seedFile", self.seedFile)
+    moduleinstance.setValue("eventType", self.eventType)
+
+  def _checkWorkflowConsistency(self):
+    return self._checkRequiredApp()
diff --git a/Interfaces/API/NewInterface/Applications/__init__.py b/Interfaces/API/NewInterface/Applications/__init__.py
index b324b62fe3f67bd4a405b0e5ced05b152ada2970..6e07f93dd47314bbbc56b6888c78526e40ef660c 100644
--- a/Interfaces/API/NewInterface/Applications/__init__.py
+++ b/Interfaces/API/NewInterface/Applications/__init__.py
@@ -39,7 +39,9 @@ __all__ = ['GenericApplication', 'GetSRMFile', '_Root', 'RootScript', 'RootMacro
            'Whizard', 'Pythia', 'PostGenSelection', 'StdhepCut', 'StdhepCutJava',
            'Mokka', 'SLIC', 'OverlayInput', 'Marlin', 'LCSIM', 'SLICPandora',
            'CheckCollections', 'SLCIOConcatenate', 'SLCIOSplit', 'StdHepSplit',
-           'Tomato', 'CheckWNs', 'DDSim', 'Fcc', 'FccSw', 'FccAnalysis', 'Whizard2']
+           'Tomato', 'CheckWNs', 'DDSim', 'Fcc', 'FccSw', 'FccAnalysis', 'Whizard2',
+           'KKMC',
+           ]
 
 from ILCDIRAC.Interfaces.API.NewInterface.Applications.GenericApplication import GenericApplication
 from ILCDIRAC.Interfaces.API.NewInterface.Applications.GetSRMFile import GetSRMFile
@@ -68,3 +70,4 @@ from ILCDIRAC.Interfaces.API.NewInterface.Applications.Fcc import Fcc
 from ILCDIRAC.Interfaces.API.NewInterface.Applications.Fcc import FccSw
 from ILCDIRAC.Interfaces.API.NewInterface.Applications.Fcc import FccAnalysis
 from ILCDIRAC.Interfaces.API.NewInterface.Applications.Whizard2 import Whizard2
+from ILCDIRAC.Interfaces.API.NewInterface.Applications.KKMC import KKMC
diff --git a/Interfaces/API/NewInterface/Tests/Test_KKMC.py b/Interfaces/API/NewInterface/Tests/Test_KKMC.py
new file mode 100644
index 0000000000000000000000000000000000000000..002a2c5610c5df21fc1622221bdd8c418018fbfc
--- /dev/null
+++ b/Interfaces/API/NewInterface/Tests/Test_KKMC.py
@@ -0,0 +1,176 @@
+#!/usr/local/env python
+"""
+Test KKMC module
+
+"""
+
+from __future__ import print_function
+import linecache
+import unittest
+from mock import patch, MagicMock as Mock
+from mock import mock_open
+from mock import mock as mock_module
+
+from parameterized import parameterized
+
+from DIRAC import gLogger, S_OK, S_ERROR
+from ILCDIRAC.Interfaces.API.NewInterface.Applications import KKMC
+from ILCDIRAC.Tests.Utilities.GeneralUtils import assertEqualsImproved, assertDiracFailsWith, \
+    assertDiracSucceeds
+
+__RCSID__ = "$Id$"
+
+MODULE_NAME = 'ILCDIRAC.Interfaces.API.NewInterface.Applications.KKMC'
+
+gLogger.setLevel("DEBUG")
+gLogger.showHeaders(True)
+
+# pylint: disable=protected-access
+
+
+class KKMCTestCase(unittest.TestCase):
+  """ Base class for the KKMC test cases
+  """
+
+  @classmethod
+  def setUpClass(cls):
+    """Load the Application file into the linecache to prevent exceptions when mocking the builtin open."""
+    from ILCDIRAC.Interfaces.API.NewInterface import Application
+    for fName in [Application.__file__, mock_module.__file__]:
+      if fName.endswith(('.pyc', '.pyo')):
+        fName = fName[:-1]
+      linecache.getlines(fName)
+
+  @classmethod
+  def tearDownClass(cls):
+    """Remove all entries from linecache because we mock builtin open."""
+    linecache.clearcache()
+
+  def setUp(self):
+    """set up the objects"""
+    self.kkmc = KKMC({})
+    self.kkmc._ops = Mock(name='OpsMock')
+
+  def test_setEvtType(self):
+    self.assertFalse(self.kkmc._errorDict)
+    self.kkmc.setEvtType('Mu')
+    self.assertFalse(self.kkmc._errorDict)
+    assertEqualsImproved(self.kkmc.eventType, 'Mu', self)
+
+  @parameterized.expand([(15, False, '_checkArgs'),
+                         ('Mu', True, 'setEvtType'),
+                         ])
+  def test_setEvtType_fail(self, evtType, addedToJob, errorMessage):
+    self.assertFalse(self.kkmc._errorDict)
+    self.kkmc.addedtojob = addedToJob
+    self.kkmc.setEvtType(evtType)
+    self.assertIn(errorMessage, self.kkmc._errorDict)
+
+  @patch('os.path.isfile', new=Mock(return_value=True))
+  @patch('__builtin__.open', mock_open(read_data='configFile content'))
+  def test_setConfigFile(self):
+    """Test setConfigFile."""
+    self.assertFalse(self.kkmc._errorDict)
+    self.kkmc.setConfigFile('/some/path/configFile.input')
+    self.assertFalse(self.kkmc._errorDict)
+    assertEqualsImproved(self.kkmc.kkmcConfigFile, 'configFile content', self)
+
+  def test_checkworkflow_app_missing(self):
+    self.kkmc._inputapp = ['some_depdency', 'unavailable_dependency_fail_on_this']
+    self.kkmc._jobapps = ['myjobapp_1', 'some_dependency']
+    assertDiracFailsWith(self.kkmc._checkWorkflowConsistency(), 'job order not correct', self)
+
+  def test_checkworkflow_empty(self):
+    self.kkmc._inputapp = []
+    self.kkmc._jobapps = []
+    assertDiracSucceeds(self.kkmc._checkWorkflowConsistency(), self)
+
+  def test_userjobmodules(self):
+    module_mock = Mock()
+    assertDiracSucceeds(self.kkmc._userjobmodules(module_mock), self)
+
+  def test_prodjobmodules(self):
+    module_mock = Mock()
+    assertDiracSucceeds(self.kkmc._prodjobmodules(module_mock), self)
+
+  def test_userjobmodules_fails(self):
+    with patch('%s._setUserJobFinalization' % MODULE_NAME, new=Mock(return_value=S_OK('something'))),\
+            patch('%s._setApplicationModuleAndParameters' % MODULE_NAME, new=Mock(return_value=S_ERROR('some_test_err'))):
+      assertDiracFailsWith(self.kkmc._userjobmodules(None),
+                           'userjobmodules failed', self)
+
+  def test_prodjobmodules_fails(self):
+    with patch('%s._setApplicationModuleAndParameters' % MODULE_NAME, new=Mock(return_value=S_OK('something'))), \
+            patch('%s._setOutputComputeDataList' % MODULE_NAME, new=Mock(return_value=S_ERROR('some_other_test_err'))):
+      assertDiracFailsWith(self.kkmc._prodjobmodules(None),
+                           'prodjobmodules failed', self)
+
+  def test_checkconsistency_configFile(self):
+    self.kkmc.version = 'LCG_97a_FCC_4'
+    self.kkmc.kkmcConfigFile = 'kkmc_steer.input'
+    assertDiracSucceeds(self.kkmc._checkConsistency(Mock()), self)
+
+  def test_checkconsistency_parameters(self):
+    self.kkmc.version = 'LCG_97a_FCC_4'
+    self.kkmc.kkmcConfigFile = None
+    self.kkmc.eventType = 'Mu'
+    self.kkmc.energy = '91.2'
+    self.kkmc.numberOfEvents = '1000'
+    self.kkmc.outputFile = 'kkmu_1000.LHE'
+    assertDiracSucceeds(self.kkmc._checkConsistency(Mock()), self)
+
+  def test_checkconsistency_noVersion(self):
+    self.kkmc.version = None
+    assertDiracFailsWith(self.kkmc._checkConsistency(Mock()), 'No version found!', self)
+
+  def test_checkconsistency_noConfigFile(self):
+    self.kkmc.version = 'LCG_97a_FCC_4'
+    self.kkmc.kkmcConfigFile = None
+    assertDiracFailsWith(self.kkmc._checkConsistency(Mock()), 'No config file set!', self)
+
+  def test_checkconsistency_noEventType(self):
+    self.kkmc.version = 'LCG_97a_FCC_4'
+    self.kkmc.kkmcConfigFile = None
+    self.kkmc.eventType = None
+    self.kkmc.energy = 91.2
+    self.kkmc.numberOfEvents = 1000
+    self.kkmc.outputFile = 'kkmu_1000.LHE'
+    assertDiracFailsWith(self.kkmc._checkConsistency(Mock()), 'No event type set!', self)
+
+  def test_checkconsistency_noEnergy(self):
+    self.kkmc.version = 'LCG_97a_FCC_4'
+    self.kkmc.kkmcConfigFile = None
+    self.kkmc.eventType = 'Mu'
+    self.kkmc.energy = None
+    self.kkmc.numberOfEvents = 1000
+    self.kkmc.outputFile = 'kkmu_1000.LHE'
+    assertDiracFailsWith(self.kkmc._checkConsistency(Mock()), 'No energy set!', self)
+
+  def test_checkconsistency_noNumberOfEvents(self):
+    self.kkmc.version = 'LCG_97a_FCC_4'
+    self.kkmc.kkmcConfigFile = None
+    self.kkmc.eventType = 'Mu'
+    self.kkmc.energy = 91.2
+    self.kkmc.numberOfEvents = None
+    self.kkmc.outputFile = 'kkmu_1000.LHE'
+    assertDiracFailsWith(self.kkmc._checkConsistency(Mock()), 'No number of events set!', self)
+
+  def test_checkconsistency_noOutputFile(self):
+    self.kkmc.version = 'LCG_97a_FCC_4'
+    self.kkmc.kkmcConfigFile = None
+    self.kkmc.eventType = 'Mu'
+    self.kkmc.energy = 91.2
+    self.kkmc.numberOfEvents = 1000
+    self.kkmc.outputFile = None
+    assertDiracFailsWith(self.kkmc._checkConsistency(Mock()), 'No output file set!', self)
+
+
+def runTests():
+  """Runs our tests"""
+  suite = unittest.defaultTestLoader.loadTestsFromTestCase(KKMCTestCase)
+  testResult = unittest.TextTestRunner(verbosity=2).run(suite)
+  print(testResult)
+
+
+if __name__ == '__main__':
+  runTests()
diff --git a/Workflow/Modules/KKMCAnalysis.py b/Workflow/Modules/KKMCAnalysis.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa55d3fd68bb474c7539d9c59ad0da0cd2c973ed
--- /dev/null
+++ b/Workflow/Modules/KKMCAnalysis.py
@@ -0,0 +1,159 @@
+'''
+Run KKMC
+
+:author: Andrea Stano
+:since:  February 03, 2021
+'''
+
+import os
+
+from DIRAC.Core.Utilities.Subprocess import shellCall
+from DIRAC.DataManagementSystem.Client.DataManager import DataManager
+from DIRAC import S_OK, S_ERROR, gLogger
+from ILCDIRAC.Workflow.Modules.ModuleBase import ModuleBase
+from ILCDIRAC.Core.Utilities.CombinedSoftwareInstallation import getEnvironmentScript, extractTarball
+from ILCDIRAC.Core.Utilities.resolvePathsAndNames import getProdFilename
+
+__RCSID__ = '$Id$'
+LOG = gLogger.getSubLogger(__name__)
+
+
+class KKMCAnalysis(ModuleBase):
+  """
+  Specific Module to run a KKMC job.
+  """
+
+  def __init__(self):
+    super(KKMCAnalysis, self).__init__()
+    self.enable = True
+    self.STEP_NUMBER = ''
+    self.result = S_ERROR()
+    self.applicationName = 'kkmc'
+    self.startFrom = 0
+    self.kkmcConfigFile = ''
+    self.seedFile = ''
+    self.eventType = ''
+    # self.eventstring = ['+++ Generating event']
+    self.datMan = DataManager()
+
+  def applicationSpecificInputs(self):
+    """ Resolve all input variables for the module here.
+
+    :return: S_OK()
+    """
+
+    if "IS_PROD" in self.workflow_commons and self.workflow_commons["IS_PROD"]:
+      self.OutputFile = getProdFilename(self.OutputFile,
+                                        int(self.workflow_commons["PRODUCTION_ID"]),
+                                        int(self.workflow_commons["JOB_ID"]),
+                                        self.workflow_commons,
+                                        )
+
+    return S_OK('Parameters resolved')
+
+  def runIt(self):
+    """
+    Called by JobAgent
+
+    Execute the following:
+      - get the environment variables that should have been set during installation
+      - prepare the steering file and command line parameters
+      - run KKMC on this steering file and catch the exit status
+
+    :rtype: :func:`~DIRAC.Core.Utilities.ReturnValues.S_OK`, :func:`~DIRAC.Core.Utilities.ReturnValues.S_ERROR`
+    """
+    self.result = S_OK()
+    if not self.platform:
+      self.result = S_ERROR('No ILC platform selected')
+    elif not self.applicationLog:
+      self.result = S_ERROR('No Log file provided')
+    if not self.result['OK']:
+      LOG.error("Failed to resolve input parameters:", self.result['Message'])
+      return self.result
+
+    if not self.workflowStatus['OK'] or not self.stepStatus['OK']:
+      LOG.verbose('Workflow status = %s, step status = %s' % (self.workflowStatus['OK'], self.stepStatus['OK']))
+      return S_OK('KKMC should not proceed as previous step did not end properly')
+
+    # get the enviroment script
+    res = getEnvironmentScript(
+        self.platform,
+        self.applicationName,
+        self.applicationVersion,
+        S_ERROR("No init script provided in CVMFS!"))
+    if not res['OK']:
+      LOG.error("Could not obtain the environment script: ", res["Message"])
+      return res
+    envScriptPath = res["Value"]
+
+    CLIArguments = ''
+
+    if self.kkmcConfigFile:
+      kkmcSteerName = 'KKMC_%s_Steer_%s.input' % (self.applicationVersion, self.STEP_NUMBER)
+      if os.path.exists(kkmcSteerName):
+        os.remove(kkmcSteerName)
+
+      kkmcSteer = []
+      kkmcSteer.append(self.kkmcConfigFile)
+
+      with open(kkmcSteerName, 'w') as steerFile:
+        steerFile.write("\n".join(kkmcSteer))
+
+      CLIArguments += '--config %s ' % kkmcSteerName
+    else:
+      CLIArguments += '--flavour %s ' % self.eventType
+      CLIArguments += '--ecms %s ' % self.energy
+      CLIArguments += '--nevts %s ' % self.NumberOfEvents
+      CLIArguments += '--outfile %s ' % self.OutputFile
+
+    if self.seedFile:
+      seedName = 'KKMC_%s_Seed_%s' % (self.applicationVersion, self.STEP_NUMBER)
+      if os.path.exists(seedName):
+        os.remove(seedName)
+      kkmcSeed = []
+      kkmcSeed.append(self.seedFile)
+
+      with open(seedName, 'w') as seedFile:
+        seedFile.write("\n".join(kkmcSeed))
+
+      CLIArguments += '--seedfile %s ' % seedName
+
+    scriptName = 'kkmc_%s_Run_%s.sh' % (self.applicationVersion, self.STEP_NUMBER)
+    if os.path.exists(scriptName):
+      os.remove(scriptName)
+    script = []
+    script.append('#!/bin/bash')
+    script.append('#####################################################################')
+    script.append('# Dynamically generated script to run a production or analysis job. #')
+    script.append('#####################################################################')
+    script.append('source %s' % envScriptPath)
+    script.append('echo =========')
+    script.append('env | sort >> localEnv.log')
+    script.append('echo kkmc:`which KKMCee`')
+    script.append('echo =========')
+    script.append('KKMCee %s' % CLIArguments)
+    script.append('declare -x appstatus=$?')
+    script.append('exit $appstatus')
+
+    with open(scriptName, 'w') as scriptFile:
+      scriptFile.write("\n".join(script))
+
+    if os.path.exists(self.applicationLog):
+      os.remove(self.applicationLog)
+
+    os.chmod(scriptName, 0o755)
+    comm = 'bash "./%s"' % scriptName
+    self.setApplicationStatus('KKMC %s step %s' % (self.applicationVersion, self.STEP_NUMBER))
+    self.stdError = ''
+    self.result = shellCall(0, comm, callbackFunction=self.redirectLogOutput, bufferLimit=20971520)
+    resultTuple = self.result['Value']
+    if not os.path.exists(self.applicationLog):
+      LOG.error("Something went terribly wrong, the log file is not present")
+      self.setApplicationStatus('%s failed to produce log file' % (self.applicationName))
+      if not self.ignoreapperrors:
+        return S_ERROR('%s did not produce the expected log %s' % (self.applicationName, self.applicationLog))
+    status = resultTuple[0]
+
+    LOG.info("Status after the application execution is %s" % status)
+
+    return self.finalStatusReport(status)
diff --git a/Workflow/Modules/Test/Test_KKMCAnalysis.py b/Workflow/Modules/Test/Test_KKMCAnalysis.py
new file mode 100644
index 0000000000000000000000000000000000000000..cdcbdc1669ccc2528a9b66137c90190f96a9a00e
--- /dev/null
+++ b/Workflow/Modules/Test/Test_KKMCAnalysis.py
@@ -0,0 +1,205 @@
+#!/usr/bin/env python
+"""Test the KKMC WorkflowModule"""
+
+from __future__ import print_function
+import __builtin__
+import unittest
+import os
+import os.path
+import shutil
+import tempfile
+from mock import patch, MagicMock as Mock, mock_open
+
+from parameterized import parameterized, param
+
+from DIRAC import gLogger, S_OK, S_ERROR
+from ILCDIRAC.Workflow.Modules.KKMCAnalysis import KKMCAnalysis
+from ILCDIRAC.Tests.Utilities.GeneralUtils import assertDiracSucceeds
+
+__RCSID__ = "$Id$"
+
+MODULE_NAME = 'ILCDIRAC.Workflow.Modules.KKMCAnalysis'
+MODULEBASE_NAME = 'ILCDIRAC.Workflow.Modules.ModuleBase'
+PROXYINFO_NAME = 'DIRAC.Core.Security.ProxyInfo'
+# pylint: disable=too-many-public-methods, protected-access
+
+gLogger.setLevel("ERROR")
+gLogger.showHeaders(True)
+
+
+def cleanup(tempdir):
+  """
+  Remove files after run
+  """
+  try:
+    shutil.rmtree(tempdir)
+  except OSError:
+    pass
+
+
+@patch("%s.getProxyInfoAsString" % MODULEBASE_NAME, new=Mock(return_value=S_OK()))
+@patch("%s.getProxyInfoAsString" % PROXYINFO_NAME, new=Mock(return_value=S_OK()))
+class TestKKMCAnalysis(unittest.TestCase):
+  """ test KKMCAnalysis """
+
+  def assertIn(self, *args, **kwargs):
+    """make this existing to placate pylint"""
+    return super(TestKKMCAnalysis, self).assertIn(*args, **kwargs)
+
+  @patch("%s.getProxyInfoAsString" % MODULEBASE_NAME, new=Mock(return_value=S_OK()))
+  @patch("%s.getProxyInfoAsString" % PROXYINFO_NAME, new=Mock(return_value=S_OK()))
+  def setUp(self):
+    self.kkmc = KKMCAnalysis()
+    self.curdir = os.getcwd()
+    self.tempdir = tempfile.mkdtemp("", dir="./")
+    os.chdir(self.tempdir)
+    self.kkmc.ops = Mock()
+
+  def tearDown(self):
+    os.chdir(self.curdir)
+    cleanup(self.tempdir)
+
+
+class TestKKMCAnalysisRunit(TestKKMCAnalysis):
+  """ test KKMC runtIt """
+
+  def setUp(self):
+    super(TestKKMCAnalysisRunit, self).setUp()
+    self.logFileName = "localEnv.log"
+    with open(self.logFileName, "w") as logF:
+      logF.write("logged the logging logs")
+
+  @patch("%s.getEnvironmentScript" % MODULE_NAME, new=Mock(return_value=S_OK("setup.sh")))
+  @patch("%s.shellCall" % MODULE_NAME, new=Mock(return_value=S_OK((0, "AllGood"))))
+  def test_KKMC_runIt_success(self):
+    """KKMC.runit ................................................................................."""
+    self.kkmc.platform = 'Windows'
+    self.kkmc.applicationLog = self.logFileName
+    # side effect for Script, userlibs, log, logAfter
+    with patch("os.path.exists", new=Mock(side_effect=[False, False, True, True])):
+      res = self.kkmc.runIt()
+    print(res)
+    assertDiracSucceeds(res, self)
+
+  @patch("%s.getEnvironmentScript" % MODULE_NAME, new=Mock(return_value=S_OK("setup.sh")))
+  @patch("%s.shellCall" % MODULE_NAME, new=Mock(return_value=S_OK((0, "AllGood"))))
+  def test_KKMC_runIt_failure_LogFile(self):
+    """KKMC.runit failure with applicationLog......................................................"""
+    self.kkmc.platform = "Windows"
+    self.kkmc.applicationLog = self.logFileName
+    self.kkmc.ignoreapperrors = False
+    # side effect for Script, userlibs, log, logAfter
+    with patch("os.path.exists", new=Mock(side_effect=[False, False, False, False])):
+      res = self.kkmc.runIt()
+    self.assertIn("did not produce the expected log", res['Message'])
+
+  @patch("%s.getEnvironmentScript" % MODULE_NAME, new=Mock(return_value=S_OK("setup.sh")))
+  @patch("%s.shellCall" % MODULE_NAME, new=Mock(return_value=S_OK((0, "AllGood"))))
+  def test_KKMC_runIt_failure_LogFile_ignore(self):
+    """KKMC.runit failure with applicationLog but ignore..........................................."""
+    self.kkmc.platform = "Windows"
+    self.kkmc.applicationLog = self.logFileName
+    self.kkmc.ignoreapperrors = True
+    # side effect for Script, userlibs, log, logAfter
+    with patch("os.path.exists", new=Mock(side_effect=[False, False, False, False])):
+      res = self.kkmc.runIt()
+    assertDiracSucceeds(res, self)
+
+  @patch("%s.getEnvironmentScript" % MODULE_NAME, new=Mock(return_value=S_OK("setup.sh")))
+  @patch("%s.shellCall" % MODULE_NAME, new=Mock(return_value=S_OK((0, "AllGood"))))
+  def test_KKMC_runIt_failure_NoLogFile(self):
+    """KKMC.runit failure with applicationLog not set............................................."""
+    self.kkmc.platform = "Windows"
+    self.kkmc.ignoreapperrors = True
+    # side effect for Script, userlibs, log, logAfter
+    with patch("os.path.exists", new=Mock(side_effect=[False, False, False, False])):
+      res = self.kkmc.runIt()
+    self.assertIn("No Log file provide", res['Message'])
+
+  @patch("%s.getEnvironmentScript" % MODULE_NAME, new=Mock(return_value=S_OK("setup.sh")))
+  @patch("%s.shellCall" % MODULE_NAME, new=Mock(return_value=S_OK((0, "AllGood"))))
+  def test_KKMC_runIt_failure_NoPlatform(self):
+    """KKMC.runit failure with platform ........................................................."""
+    self.kkmc.applicationLog = self.logFileName
+    self.kkmc.ignoreapperrors = True
+    # side effect for Script, userlibs, log, logAfter
+    with patch("os.path.exists", new=Mock(side_effect=[False, False, False, False])):
+      res = self.kkmc.runIt()
+    self.assertIn("No ILC platform selected", res['Message'])
+
+  @patch("%s.getEnvironmentScript" % MODULE_NAME, new=Mock(return_value=S_OK("setup.sh")))
+  @patch("%s.shellCall" % MODULE_NAME, new=Mock(return_value=S_OK((0, "AllGood"))))
+  def test_KKMC_runIt_success_LogAndScriptPresent(self):
+    """KKMC.runit success log and script exist..................................................."""
+    self.kkmc.platform = "Windows"
+    self.kkmc.applicationLog = self.logFileName
+    self.kkmc.ignoreapperrors = True
+    with open("kkmc__Run_.sh", "w") as scr:
+      scr.write("content")
+    with open("KKMC__Steer_.input", "w") as scr:
+      scr.write("content")
+    with open(self.logFileName, "w") as scr:
+      scr.write("content")
+    # side effect for Script, userlibs, log, logAfter
+    with patch("os.path.exists", new=Mock(side_effect=[True, True, False, True])):
+      res = self.kkmc.runIt()
+    assertDiracSucceeds(res, self)
+
+  # @parameterized.expand([('NumberOfEvents', 100, 'n_events = 100'),
+  #                        ('OutputFile', 'test.slcio', 'sample_format = lcio'),
+  #                        ('OutputFile', 'test.ascii', 'sample_format = ascii'),
+  #                        ('OutputFile', 'test.stdhep', 'sample_format = stdhep'),
+  #                        ('randomSeed', '321', 'seed = 321'),
+  #                        param('KKMCRawSin', True, 'sample_format', pUnExpected=True),
+  #                        ])
+  # @patch("%s.getEnvironmentScript" % MODULE_NAME, new=Mock(return_value=S_OK("setup.sh") ) )
+  # @patch("%s.shellCall" % MODULE_NAME, new=Mock(return_value=S_OK((0,"AllGood")) ) )
+  # def test_KKMC_runIt_success_configFile(self, pName, pValue, pExpected, pUnExpected=None):
+  #   """kkmc.runit success with configFile........................................................."""
+  #   self.kkmc.platform = "Windows"
+  #   self.kkmc.applicationLog = self.logFileName
+  #   self.kkmc.kkmcConfigFile  = "kkmc instructions"
+  #   setattr(self.kkmc, pName, pValue)
+  #   ## side effect for Steering1, Steering2, Script, userlib, log, logAfter
+  #   with patch("os.path.exists", new=Mock(side_effect=[False, False, False, True] ) ):
+  #     res = self.kkmc.runIt()
+  #   assertDiracSucceeds( res, self )
+  #   self.assertEqual( self.kkmc.kkmcConfigFile , "kkmc instructions" )
+  #   self.assertIn( "kkmc instructions", open("KKMC__Steer_.input").read())
+  #   if not pUnExpected:
+  #     self.assertIn(pExpected, open("KKMC__Steer_.input").read())
+  #   else:
+  #     self.assertNotIn(pExpected, open("KKMC__Steer_.input").read())
+
+  @patch("%s.getEnvironmentScript" % MODULE_NAME, new=Mock(return_value=S_ERROR("missing setup.sh")))
+  def test_KKMC_runIt_fail_env(self):
+    """kkmc.runit failed to get env................................................................"""
+    self.kkmc.platform = "Windows"
+    self.kkmc.applicationLog = self.logFileName
+    res = self.kkmc.runIt()
+    self.assertEqual(res['Message'], "missing setup.sh")
+
+
+class TestKKMCAnalysisASI(TestKKMCAnalysis):
+  """kkmc.ApplicationSpecificInputs """
+
+  @patch.dict(os.environ, {"JOBID": "12345"})
+  def test_KKMC_ASI_NoVariables(self):
+    """kkmc.applicationSpecificInputs: checks that no variables have been set after this call......"""
+    gLogger.setLevel("ERROR")
+    self.kkmc.workflow_commons = dict()
+    self.kkmc.applicationSpecificInputs()
+    self.assertFalse(self.kkmc.jobReport or self.kkmc.productionID)
+
+
+def runTests():
+  """Runs our tests"""
+  suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestKKMCAnalysis)
+  suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestKKMCAnalysisRunit))
+  suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestKKMCAnalysisASI))
+  testResult = unittest.TextTestRunner(verbosity=2).run(suite)
+  print(testResult)
+
+
+if __name__ == '__main__':
+  runTests()