Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
amrani
erp5
Commits
06055895
Commit
06055895
authored
Jun 06, 2013
by
Benjamin Blanc
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add some modifications to testERP5testnode to take into account Scalabilityrunner, not working yet
parent
b8d8de4d
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
110 additions
and
45 deletions
+110
-45
erp5/tests/testERP5TestNode.py
erp5/tests/testERP5TestNode.py
+73
-24
erp5/util/testnode/ScalabilityTestRunner.py
erp5/util/testnode/ScalabilityTestRunner.py
+29
-12
erp5/util/testnode/SlapOSControler.py
erp5/util/testnode/SlapOSControler.py
+5
-5
erp5/util/testnode/testnode.py
erp5/util/testnode/testnode.py
+3
-4
No files found.
erp5/tests/testERP5TestNode.py
View file @
06055895
...
@@ -21,6 +21,8 @@ import time
...
@@ -21,6 +21,8 @@ import time
import
types
import
types
import
re
import
re
USE_UNIT_TEST_RUNNER
=
True
class
ERP5TestNode
(
TestCase
):
class
ERP5TestNode
(
TestCase
):
def
setUp
(
self
):
def
setUp
(
self
):
...
@@ -369,7 +371,14 @@ branch = foo
...
@@ -369,7 +371,14 @@ branch = foo
return
[]
return
[]
test_node
=
self
.
getTestNode
()
test_node
=
self
.
getTestNode
()
runner
=
UnitTestRunner
(
test_node
)
if
USE_UNIT_TEST_RUNNER
:
RunnerClass
=
UnitTestRunner
else
:
RunnerClass
=
ScalabilityTestRunner
runner
=
RunnerClass
(
test_node
)
# Create and initialise/regenerate a nodetestsuite
# Create and initialise/regenerate a nodetestsuite
node_test_suite
=
test_node
.
getNodeTestSuite
(
'foo'
)
node_test_suite
=
test_node
.
getNodeTestSuite
(
'foo'
)
...
@@ -383,12 +392,21 @@ branch = foo
...
@@ -383,12 +392,21 @@ branch = foo
def
checkRunTestSuiteParameters
(
additional_parameter_list
=
None
):
def
checkRunTestSuiteParameters
(
additional_parameter_list
=
None
):
ProcessManager
.
getSupportedParameterSet
=
patch_getSupportedParameterSet
ProcessManager
.
getSupportedParameterSet
=
patch_getSupportedParameterSet
ProcessManager
.
spawn
=
get_parameters
ProcessManager
.
spawn
=
get_parameters
runner
=
UnitTestRunner
(
test_node
)
if
USE_UNIT_TEST_RUNNER
:
RunnerClass
=
UnitTestRunner
else
:
RunnerClass
=
ScalabilityTestRunner
runner
=
RunnerClass
(
test_node
)
runner
.
runTestSuite
(
node_test_suite
,
"http://foo.bar"
)
runner
.
runTestSuite
(
node_test_suite
,
"http://foo.bar"
)
if
USE_UNIT_TEST_RUNNER
:
expected_parameter_list
=
[
'%s/a/bin/runTestSuite'
expected_parameter_list
=
[
'%s/a/bin/runTestSuite'
%
(
runner
.
slapos_controler
.
instance_root
),
'--test_suite'
,
'Foo'
,
'--revision'
,
%
(
runner
.
slapos_controler
.
instance_root
),
'--test_suite'
,
'Foo'
,
'--revision'
,
'dummy'
,
'--test_suite_title'
,
'Foo-Test'
,
'--node_quantity'
,
3
,
'--master_url'
,
'dummy'
,
'--test_suite_title'
,
'Foo-Test'
,
'--node_quantity'
,
3
,
'--master_url'
,
'http://foo.bar'
]
'http://foo.bar'
]
else
:
expected_parameter_list
=
[]
if
additional_parameter_list
:
if
additional_parameter_list
:
expected_parameter_list
.
extend
(
additional_parameter_list
)
expected_parameter_list
.
extend
(
additional_parameter_list
)
self
.
assertEqual
(
call_parameter_list
[
0
][
'args'
],
expected_parameter_list
)
self
.
assertEqual
(
call_parameter_list
[
0
][
'args'
],
expected_parameter_list
)
...
@@ -412,7 +430,13 @@ branch = foo
...
@@ -412,7 +430,13 @@ branch = foo
def
test_10_prepareSlapOS
(
self
):
def
test_10_prepareSlapOS
(
self
):
test_node
=
self
.
getTestNode
()
test_node
=
self
.
getTestNode
()
test_node_slapos
=
SlapOSInstance
()
test_node_slapos
=
SlapOSInstance
()
runner
=
UnitTestRunner
(
test_node
)
if
USE_UNIT_TEST_RUNNER
:
RunnerClass
=
UnitTestRunner
else
:
RunnerClass
=
ScalabilityTestRunner
runner
=
RunnerClass
(
test_node
)
node_test_suite
=
test_node
.
getNodeTestSuite
(
'foo'
)
node_test_suite
=
test_node
.
getNodeTestSuite
(
'foo'
)
node_test_suite
.
edit
(
working_directory
=
self
.
working_directory
)
node_test_suite
.
edit
(
working_directory
=
self
.
working_directory
)
status_dict
=
{
"status_code"
:
0
}
status_dict
=
{
"status_code"
:
0
}
...
@@ -428,17 +452,29 @@ branch = foo
...
@@ -428,17 +452,29 @@ branch = foo
"args"
:
[
x
for
x
in
args
],
"args"
:
[
x
for
x
in
args
],
"kw"
:
kw
})
"kw"
:
kw
})
return
{
"status_code"
:
self
.
status_code
}
return
{
"status_code"
:
self
.
status_code
}
if
USE_UNIT_TEST_RUNNER
:
SlapOSControler
.
initializeSlapOSControler
=
Patch
(
"initializeSlapOSControler"
)
SlapOSControler
.
initializeSlapOSControler
=
Patch
(
"initializeSlapOSControler"
)
SlapOSControler
.
runSoftwareRelease
=
Patch
(
"runSoftwareRelease"
)
SlapOSControler
.
runSoftwareRelease
=
Patch
(
"runSoftwareRelease"
)
SlapOSControler
.
runComputerPartition
=
Patch
(
"runComputerPartition"
)
SlapOSControler
.
runComputerPartition
=
Patch
(
"runComputerPartition"
)
method_list_for_prepareSlapOSForTestNode
=
[
"initializeSlapOSControler"
,
"runSoftwareRelease"
]
method_list_for_prepareSlapOSForTestSuite
=
[
"initializeSlapOSControler"
,
"runSoftwareRelease"
,
"runComputerPartition"
]
else
:
SlapOSControler
.
runComputerPartition
=
Patch
(
"supply"
)
method_list_for_prepareSlapOSForTestNode
=
[
"supply"
]
method_list_for_prepareSlapOSForTestSuite
=
[
"supply"
]
runner
.
prepareSlapOSForTestNode
(
test_node_slapos
)
runner
.
prepareSlapOSForTestNode
(
test_node_slapos
)
self
.
assertEquals
([
"initializeSlapOSControler"
,
"runSoftwareRelease"
],
self
.
assertEquals
(
method_list_for_prepareSlapOSForTestNode
,
[
x
[
"method_name"
]
for
x
in
call_list
])
[
x
[
"method_name"
]
for
x
in
call_list
])
call_list
=
[]
call_list
=
[]
runner
.
prepareSlapOSForTestSuite
(
node_test_suite
)
runner
.
prepareSlapOSForTestSuite
(
node_test_suite
)
self
.
assertEquals
([
"initializeSlapOSControler"
,
"runSoftwareRelease"
,
self
.
assertEquals
(
method_list_for_prepareSlapOSForTestSuite
,
"runComputerPartition"
],
[
x
[
"method_name"
]
for
x
in
call_list
])
[
x
[
"method_name"
]
for
x
in
call_list
])
call_list
=
[]
call_list
=
[]
SlapOSControler
.
runSoftwareRelease
=
Patch
(
"runSoftwareRelease"
,
status_code
=
1
)
SlapOSControler
.
runSoftwareRelease
=
Patch
(
"runSoftwareRelease"
,
status_code
=
1
)
...
@@ -499,6 +535,11 @@ branch = foo
...
@@ -499,6 +535,11 @@ branch = foo
time
.
sleep
=
doNothing
time
.
sleep
=
doNothing
self
.
generateTestRepositoryList
()
self
.
generateTestRepositoryList
()
if
USE_UNIT_TEST_RUNNER
:
RunnerClass
=
UnitTestRunner
else
:
RunnerClass
=
ScalabilityTestRunner
# Patch
# Patch
original_startTestSuite
=
TaskDistributor
.
startTestSuite
original_startTestSuite
=
TaskDistributor
.
startTestSuite
TaskDistributor
.
startTestSuite
=
patch_startTestSuite
TaskDistributor
.
startTestSuite
=
patch_startTestSuite
...
@@ -509,10 +550,10 @@ branch = foo
...
@@ -509,10 +550,10 @@ branch = foo
test_node
=
self
.
getTestNode
()
test_node
=
self
.
getTestNode
()
# Modify class UnitTestRunner(or more after) method
# Modify class UnitTestRunner(or more after) method
original_prepareSlapOS
=
UnitTestRunner
.
_prepareSlapOS
original_prepareSlapOS
=
RunnerClass
.
_prepareSlapOS
original_runTestSuite
=
UnitTestRunner
.
runTestSuite
original_runTestSuite
=
RunnerClass
.
runTestSuite
UnitTestRunner
.
_prepareSlapOS
=
doNothing
RunnerClass
.
_prepareSlapOS
=
doNothing
UnitTestRunner
.
runTestSuite
=
doNothing
RunnerClass
.
runTestSuite
=
doNothing
SlapOSControler
.
initializeSlapOSControler
=
doNothing
SlapOSControler
.
initializeSlapOSControler
=
doNothing
# Inside test_node a runner is created using new UnitTestRunner methods
# Inside test_node a runner is created using new UnitTestRunner methods
...
@@ -522,8 +563,8 @@ branch = foo
...
@@ -522,8 +563,8 @@ branch = foo
# Restore old class methods
# Restore old class methods
TaskDistributor
.
startTestSuite
=
original_startTestSuite
TaskDistributor
.
startTestSuite
=
original_startTestSuite
TaskDistributionTool
.
createTestResult
=
original_createTestResult
TaskDistributionTool
.
createTestResult
=
original_createTestResult
UnitTestRunner
.
_prepareSlapOS
=
original_prepareSlapOS
RunnerClass
.
_prepareSlapOS
=
original_prepareSlapOS
UnitTestRunner
.
runTestSuite
=
original_runTestSuite
RunnerClass
.
runTestSuite
=
original_runTestSuite
def
test_12_spawn
(
self
):
def
test_12_spawn
(
self
):
def
_checkCorrectStatus
(
expected_status
,
*
args
):
def
_checkCorrectStatus
(
expected_status
,
*
args
):
...
@@ -606,6 +647,11 @@ branch = foo
...
@@ -606,6 +647,11 @@ branch = foo
self
.
assertEquals
(
1
,
len
([
x
for
x
in
suite_log
.
readlines
()
\
self
.
assertEquals
(
1
,
len
([
x
for
x
in
suite_log
.
readlines
()
\
if
x
.
find
(
"Activated logfile"
)
>=
0
]))
if
x
.
find
(
"Activated logfile"
)
>=
0
]))
if
USE_UNIT_TEST_RUNNER
:
RunnerClass
=
UnitTestRunner
else
:
RunnerClass
=
ScalabilityTestRunner
original_sleep
=
time
.
sleep
original_sleep
=
time
.
sleep
time
.
sleep
=
doNothing
time
.
sleep
=
doNothing
self
.
generateTestRepositoryList
()
self
.
generateTestRepositoryList
()
...
@@ -615,10 +661,10 @@ branch = foo
...
@@ -615,10 +661,10 @@ branch = foo
TaskDistributionTool
.
createTestResult
=
patch_createTestResult
TaskDistributionTool
.
createTestResult
=
patch_createTestResult
test_node
=
self
.
getTestNode
()
test_node
=
self
.
getTestNode
()
# Change UnitTestRunner class methods
# Change UnitTestRunner class methods
original_prepareSlapOS
=
UnitTestRunner
.
_prepareSlapOS
original_prepareSlapOS
=
RunnerClass
.
_prepareSlapOS
UnitTestRunner
.
_prepareSlapOS
=
doNothing
RunnerClass
.
_prepareSlapOS
=
doNothing
original_runTestSuite
=
UnitTestRunner
.
runTestSuite
original_runTestSuite
=
RunnerClass
.
runTestSuite
UnitTestRunner
.
runTestSuite
=
doNothing
RunnerClass
.
runTestSuite
=
doNothing
SlapOSControler
.
initializeSlapOSControler
=
doNothing
SlapOSControler
.
initializeSlapOSControler
=
doNothing
test_node
.
run
()
test_node
.
run
()
...
@@ -630,8 +676,8 @@ branch = foo
...
@@ -630,8 +676,8 @@ branch = foo
# Restore old class methods
# Restore old class methods
TaskDistributor
.
startTestSuite
=
original_startTestSuite
TaskDistributor
.
startTestSuite
=
original_startTestSuite
TaskDistributionTool
.
createTestResult
=
original_createTestResult
TaskDistributionTool
.
createTestResult
=
original_createTestResult
UnitTestRunner
.
_prepareSlapOS
=
original_prepareSlapOS
RunnerClass
.
_prepareSlapOS
=
original_prepareSlapOS
UnitTestRunner
.
runTestSuite
=
original_runTestSuite
RunnerClass
.
runTestSuite
=
original_runTestSuite
def
test_16_cleanupLogDirectory
(
self
):
def
test_16_cleanupLogDirectory
(
self
):
# Make sure that we are able to cleanup old log folders
# Make sure that we are able to cleanup old log folders
...
@@ -684,8 +730,11 @@ branch = foo
...
@@ -684,8 +730,11 @@ branch = foo
SlapOSControler
.
initializeSlapOSControler
SlapOSControler
.
initializeSlapOSControler
initial_runSoftwareRelease
=
SlapOSControler
.
runSoftwareRelease
initial_runSoftwareRelease
=
SlapOSControler
.
runSoftwareRelease
test_node
=
self
.
getTestNode
()
test_node
=
self
.
getTestNode
()
runner
=
UnitTestRunner
(
test_node
)
if
USE_UNIT_TEST_RUNNER
:
RunnerClass
=
UnitTestRunner
else
:
RunnerClass
=
ScalabilityTestRunner
runner
=
RunnerClass
(
test_node
)
node_test_suite
=
test_node
.
getNodeTestSuite
(
'foo'
)
node_test_suite
=
test_node
.
getNodeTestSuite
(
'foo'
)
init_call_kw_list
=
[]
init_call_kw_list
=
[]
def
initializeSlapOSControler
(
self
,
**
kw
):
def
initializeSlapOSControler
(
self
,
**
kw
):
...
...
erp5/util/testnode/ScalabilityTestRunner.py
View file @
06055895
...
@@ -45,6 +45,10 @@ from erp5.util import taskdistribution
...
@@ -45,6 +45,10 @@ from erp5.util import taskdistribution
class
ScalabilityTestRunner
():
class
ScalabilityTestRunner
():
def
__init__
(
self
,
testnode
):
def
__init__
(
self
,
testnode
):
self
.
testnode
=
testnode
self
.
testnode
=
testnode
self
.
slapos_controler
=
SlapOSControler
.
SlapOSControler
(
self
.
testnode
.
working_directory
,
self
.
testnode
.
config
,
self
.
testnode
.
log
)
self
.
involved_nodes
=
[]
# doesn't change during all the test
self
.
involved_nodes
=
[]
# doesn't change during all the test
self
.
worker_nodes
=
[]
# may change between two test_suite
self
.
worker_nodes
=
[]
# may change between two test_suite
self
.
launcher_nodes
=
[]
# may change between two test_suite
self
.
launcher_nodes
=
[]
# may change between two test_suite
...
@@ -54,30 +58,41 @@ class ScalabilityTestRunner():
...
@@ -54,30 +58,41 @@ class ScalabilityTestRunner():
def
_prepareSlapOS
(
*
args
,
**
kw
):
def
_prepareSlapOS
(
*
args
,
**
kw
):
"""
"""
A proxy to
_
supply : Install software a software on a specific node
A proxy to supply : Install software a software on a specific node
"""
"""
self
.
slapos_controler
.
_supply
(
*
args
,
**
kw
)
self
.
slapos_controler
.
supply
(
*
args
,
**
kw
)
# TODO : do something with slapOS Master to check if it's ok
# put it here ?
# TODO : change the line below
return
{
'status_code'
:
0
}
def
prepareSlapOSForTestNode
(
self
):
def
prepareSlapOSForTestNode
(
self
,
test_node_slapos
=
None
):
"""
"""
Install all softwares used to run tests (ex : launcher software)
Install all softwares used to run tests (ex : launcher software)
"""
"""
for
software_path
in
self
.
config
.
get
(
"software_list"
):
software_path_list
=
[]
for
computer_guid
in
self
.
launcher_nodes
[
'computer_id'
]:
software_path_list
.
append
(
self
.
testnode
.
config
.
get
(
"software_list"
))
self
.
_prepareSlapOS
(
software_path
,
computer_guid
)
for
software_path
in
software_path_list
:
for
launcher_node
in
self
.
launcher_nodes
:
self
.
_prepareSlapOS
(
software_path
,
launcher_node
[
'computer_id'
])
# TODO : change the line below
return
{
'status_code'
:
0
}
def
_extractSoftwarePathList
(
self
,
node_test_suite
):
def
_extractSoftwarePathList
(
self
,
node_test_suite
):
# TODO : write code
# TODO : write code
return
[]
return
[]
def
prepareSlapOSForTestSuite
(
self
,
node_test_suite
):
def
prepareSlapOSForTestSuite
(
self
,
node_test_suite
):
"""
"""
Install all testsuite's softwares (on worker_nodes)
Install all testsuite's softwares (on worker_nodes)
"""
"""
# In fact we just need to extract (by knowing the ipv6)
# softwares ipv6-url ( created during constructProfile(...) )
software_path_list
=
_extractSoftwarePathList
(
software_path_list
)
software_path_list
=
_extractSoftwarePathList
(
software_path_list
)
for
software_path
in
software_path_list
:
for
software_path
in
software_path_list
:
for
computer_guid
in
self
.
worker_nodes
[
'computer_id'
]:
for
worker_node
in
self
.
worker_nodes
:
self
.
_prepareSlapOS
(
software_path
,
computer_guid
)
self
.
_prepareSlapOS
(
software_path
,
worker_node
[
'computer_id'
])
# TODO : change the line below
return
{
'status_code'
:
0
}
def
_cleanUpNodesInformation
(
self
):
def
_cleanUpNodesInformation
(
self
):
self
.
worker_nodes
=
[]
self
.
worker_nodes
=
[]
...
@@ -93,6 +108,8 @@ class ScalabilityTestRunner():
...
@@ -93,6 +108,8 @@ class ScalabilityTestRunner():
def
runTestSuite
(
self
,
node_test_suite
,
portal_url
,
log
=
None
):
def
runTestSuite
(
self
,
node_test_suite
,
portal_url
,
log
=
None
):
# TODO : write code
# TODO : write code
SlapOSControler
.
createFolder
(
node_test_suite
.
test_suite_directory
,
clean
=
True
)
pass
pass
...
...
erp5/util/testnode/SlapOSControler.py
View file @
06055895
...
@@ -73,16 +73,16 @@ class SlapOSControlerCluster(object):
...
@@ -73,16 +73,16 @@ class SlapOSControlerCluster(object):
my_controler.initializeSlapOSControler(['kvm.cfg', 'ok.cfg'], 'COMP-726')
my_controler.initializeSlapOSControler(['kvm.cfg', 'ok.cfg'], 'COMP-726')
"""
"""
for
software_path
in
software_path_list
:
for
software_path
in
software_path_list
:
self
.
_
supply
(
software_path
,
computer_guid
)
self
.
supply
(
software_path
,
computer_guid
)
def
_
supply
(
self
,
software_url
,
computer_id
):
def
supply
(
self
,
software_url
,
computer_id
):
"""
"""
Ex :
Ex :
my_controler.
_
supply('kvm.cfg', 'COMP-726')
my_controler.supply('kvm.cfg', 'COMP-726')
"""
"""
# TODO : remove return
# TODO : remove return
return
return
self
.
log
(
'SlapOSControler :
_
supply'
)
self
.
log
(
'SlapOSControler : supply'
)
parser
=
argparse
.
ArgumentParser
()
parser
=
argparse
.
ArgumentParser
()
parser
.
add_argument
(
"configuration_file"
)
parser
.
add_argument
(
"configuration_file"
)
parser
.
add_argument
(
"software_url"
)
parser
.
add_argument
(
"software_url"
)
...
@@ -90,7 +90,7 @@ class SlapOSControlerCluster(object):
...
@@ -90,7 +90,7 @@ class SlapOSControlerCluster(object):
if
os
.
path
.
exists
(
configuration_file_path
):
if
os
.
path
.
exists
(
configuration_file_path
):
args
=
parser
.
parse_args
([
self
.
configuration_file_path
,
software_url
,
computer_id
])
args
=
parser
.
parse_args
([
self
.
configuration_file_path
,
software_url
,
computer_id
])
config
=
client
.
Config
(
args
,
args
.
configuration_file
)
config
=
client
.
Config
(
args
,
args
.
configuration_file
)
client
.
_
supply
(
args
.
software_url
,
args
.
node
,
client
.
init
(
config
))
client
.
supply
(
args
.
software_url
,
args
.
node
,
client
.
init
(
config
))
else
:
else
:
raise
ValueError
(
"Configuration file not found."
)
raise
ValueError
(
"Configuration file not found."
)
...
...
erp5/util/testnode/testnode.py
View file @
06055895
...
@@ -92,6 +92,7 @@ class TestNode(object):
...
@@ -92,6 +92,7 @@ class TestNode(object):
self
.
file_handler
=
None
self
.
file_handler
=
None
self
.
max_log_time
=
max_log_time
self
.
max_log_time
=
max_log_time
self
.
max_temp_time
=
max_temp_time
self
.
max_temp_time
=
max_temp_time
self
.
url_access
=
"https://[0::0]:0123"
# Ipv6 + port of the node
def
checkOldTestSuite
(
self
,
test_suite_data
):
def
checkOldTestSuite
(
self
,
test_suite_data
):
...
@@ -340,10 +341,8 @@ branch = %(branch)s
...
@@ -340,10 +341,8 @@ branch = %(branch)s
# Select the good runner
# Select the good runner
if
True
:
if
True
:
runner
=
UnitTestRunner
(
self
)
runner
=
UnitTestRunner
(
self
)
elif
False
:
runner
=
ScalabilityTestRunner
(
self
)
else
:
else
:
runner
=
Unit
TestRunner
(
self
)
runner
=
Scalability
TestRunner
(
self
)
runner
.
prepareSlapOSForTestNode
(
test_node_slapos
)
runner
.
prepareSlapOSForTestNode
(
test_node_slapos
)
#Clean-up test suites
#Clean-up test suites
...
@@ -389,7 +388,7 @@ branch = %(branch)s
...
@@ -389,7 +388,7 @@ branch = %(branch)s
# a reliable way to check if they are up or not ...
# a reliable way to check if they are up or not ...
# time.sleep(20)
# time.sleep(20)
# For scalability test runTestSuite is a big part
# For scalability test runTestSuite is a big part
runner
.
runTestSuite
(
node_test_suite
,
portal_url
)
runner
.
runTestSuite
(
node_test_suite
,
portal_url
)
# break the loop to get latest priorities from master
# break the loop to get latest priorities from master
break
break
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment