Skip to content

Commit de7a394

Browse files
jorsanpeJordi Sanchez
andauthored
Allow using a Dockerfile for building and running tests (#253)
* Allow using a Dockerfile for building and running tests * Update cpm version to 1.8.0 Co-authored-by: Jordi Sanchez <[email protected]>
1 parent eb04b95 commit de7a394

File tree

9 files changed

+85
-12
lines changed

9 files changed

+85
-12
lines changed

cpm/domain/project/project_composer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ def compose_target(target_name, project_descriptor):
2525
target.include_directories.update(project_descriptor.build.includes)
2626
target.main = target_description.main
2727
target.image = target_description.image
28-
target.test_image = target_description.test_image
2928
target.dockerfile = target_description.dockerfile
29+
target.test_image = target_description.test_image
30+
target.test_dockerfile = target_description.test_dockerfile
3031
target.toolchain_prefix = target_description.toolchain_prefix
3132
target.post_build = target_description.post_build
3233
compose_packages(project_descriptor.build.packages, target)

cpm/domain/project/project_descriptor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class TargetDescription:
4646
dockerfile: str = ''
4747
image: str = ''
4848
test_image: str = ''
49+
test_dockerfile: str = ''
4950
toolchain_prefix: str = ''
5051

5152

cpm/domain/project/project_descriptor_parser.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ def parse_targets(targets_description):
4040
def parse_target(target_name, target_description):
4141
target = TargetDescription(target_name)
4242
target.image = target_description.get('image', '')
43-
target.test_image = target_description.get('test_image', '')
4443
target.dockerfile = target_description.get('dockerfile', '')
44+
target.test_image = target_description.get('test_image', '')
45+
target.test_dockerfile = target_description.get('test_dockerfile', '')
4546
target.toolchain_prefix = target_description.get('toolchain_prefix', '')
4647
target.format = target_description.get('format', 'binary')
4748
target.main = target_description.get('main', '')

cpm/domain/project_commands.py

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,44 @@ def build_tests(self, project, files_or_dirs):
3030
self.__build_tests(project, [test.name for test in tests_to_run])
3131

3232
def __build_tests(self, project, goals, post_build=''):
33+
if project.target.test_image and project.target.test_dockerfile:
34+
print('warning: both "test_image" and "test_dockerfile" options are specified, will use "test_image"')
3335
if project.target.test_image:
34-
self.__build_using_image(project, project.target.test_image, goals, post_build)
36+
self.__build_using_image(
37+
project,
38+
project.target.test_image,
39+
goals,
40+
post_build
41+
)
42+
elif project.target.test_dockerfile:
43+
self.__build_using_dockerfile(
44+
project,
45+
project.target.test_dockerfile,
46+
self.__test_image_name(project),
47+
goals,
48+
post_build
49+
)
3550
else:
3651
self.__build_goal(goals)
3752

3853
def __build(self, project, goals, post_build=''):
54+
if project.target.image and project.target.dockerfile:
55+
print('warning: both "image" and "dockerfile" options are specified, will use "image"')
3956
if project.target.image:
40-
self.__build_using_image(project, project.target.image, goals, post_build)
57+
self.__build_using_image(
58+
project,
59+
project.target.image,
60+
goals,
61+
post_build
62+
)
4163
elif project.target.dockerfile:
42-
self.__build_using_dockerfile(project, project.target.dockerfile, goals, post_build)
64+
self.__build_using_dockerfile(
65+
project,
66+
project.target.dockerfile,
67+
self.__build_image_name(project),
68+
goals,
69+
post_build
70+
)
4371
else:
4472
self.__build_goal(goals)
4573

@@ -61,10 +89,9 @@ def __build_using_image(self, project, image_name, goals, post_build):
6189
raise DockerImageNotFound(image_name)
6290
self.__build_inside_container(client, project, image_name, goals, post_build)
6391

64-
def __build_using_dockerfile(self, project, dockerfile, goals, post_build):
92+
def __build_using_dockerfile(self, project, dockerfile, image_name, goals, post_build):
6593
client = docker.from_env()
6694
print(f'building image from {dockerfile}')
67-
image_name = f'{project.name}_cpm_build'
6895
with open(dockerfile, 'rb') as fileobj:
6996
client.images.build(path='.', fileobj=fileobj, tag=image_name)
7097
self.__build_inside_container(client, project, image_name, goals, post_build)
@@ -92,10 +119,28 @@ def __build_inside_container(self, client, project, image_name, goals, post_buil
92119
raise BuildError
93120
container.remove()
94121

122+
def __build_image_name(self, project):
123+
return f'{project.name}_cpm_build'
124+
125+
def __test_image_name(self, project):
126+
return f'{project.name}_cpm_test'
127+
95128
def run_tests(self, project, files_or_dirs, test_args=()):
96129
tests_to_run = project.test.test_suites if not files_or_dirs else self.tests_from_args(project, files_or_dirs)
97130
if project.target.test_image:
98-
test_results = self.__run_tests_inside_container(project, tests_to_run, test_args)
131+
test_results = self.__run_tests_using_image(
132+
project,
133+
project.target.test_image,
134+
tests_to_run,
135+
test_args
136+
)
137+
elif project.target.test_dockerfile:
138+
test_results = self.__run_tests_using_dockerfile(
139+
project,
140+
self.__test_image_name(project),
141+
tests_to_run,
142+
test_args
143+
)
99144
else:
100145
test_results = [self.run_test(test.name, test_args) for test in tests_to_run]
101146
if any(result != 0 for result in test_results):
@@ -109,9 +154,8 @@ def run_test(self, executable, test_args):
109154
print(f'{executable} failed with {result.returncode} ({signal.Signals(-result.returncode).name})')
110155
return result.returncode
111156

112-
def __run_tests_inside_container(self, project, tests_to_run, test_args):
157+
def __run_tests_using_image(self, project, image_name, tests_to_run, test_args):
113158
client = docker.from_env()
114-
image_name = project.target.test_image
115159
try:
116160
client.images.pull(image_name)
117161
except docker.errors.ImageNotFound:
@@ -120,6 +164,10 @@ def __run_tests_inside_container(self, project, tests_to_run, test_args):
120164
raise DockerImageNotFound(image_name)
121165
return [self.__run_test_inside_container(project, client, image_name, test.name, test_args) for test in tests_to_run]
122166

167+
def __run_tests_using_dockerfile(self, project, image_name, tests_to_run, test_args):
168+
client = docker.from_env()
169+
return [self.__run_test_inside_container(project, client, image_name, test.name, test_args) for test in tests_to_run]
170+
123171
def __run_test_inside_container(self, project, client, image_name, executable, test_args):
124172
container = client.containers.run(
125173
image_name,
@@ -129,6 +177,8 @@ def __run_test_inside_container(self, project, client, image_name, executable, t
129177
user=f'{os.getuid()}:{os.getgid()}',
130178
detach=True
131179
)
180+
for log in container.logs(stream=True):
181+
sys.stdout.write(log.decode())
132182
exit_code = container.wait()
133183
result = exit_code['StatusCode']
134184
container.remove()

scripts/cpm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/env python3.7
1+
#!/usr/bin/env python3
22
import cpm
33

44
cpm.main()

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = cpm-cli
3-
version = 1.7.0
3+
version = 1.8.0
44
description = Chromos Package Manager
55
long_description = file: README.md
66
long_description_content_type = text/markdown

test/domain/test_project_composer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ def test_should_compose_project_from_project_description_with_one_target_build_p
9999
'main': 'main.c',
100100
'image': 'cpmbits/docker',
101101
'test_image': 'cpmbits/docker_test',
102+
'test_dockerfile': 'test.Dockerfile',
102103
'toolchain_prefix': 'arm-linux-gnueabi-'
103104
}
104105
}
@@ -119,6 +120,7 @@ def test_should_compose_project_from_project_description_with_one_target_build_p
119120
assert project.target.main == 'main.c'
120121
assert project.target.image == 'cpmbits/docker'
121122
assert project.target.test_image == 'cpmbits/docker_test'
123+
assert project.target.test_dockerfile == 'test.Dockerfile'
122124
assert project.target.toolchain_prefix == 'arm-linux-gnueabi-'
123125

124126
@mock.patch('cpm.domain.project.project_composer.filesystem')

test/domain/test_project_descriptor_parser.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def test_parse_project_descriptor_with_default_target_image(self):
8686
'image': 'cpmbits/bender',
8787
'main': 'main.c',
8888
'test_image': 'cpmbits/bender_test',
89+
'test_dockerfile': 'test.Dockerfile',
8990
'toolchain_prefix': 'arm-linux-gnueabi-'
9091
}
9192
}
@@ -97,6 +98,7 @@ def test_parse_project_descriptor_with_default_target_image(self):
9798
image='cpmbits/bender',
9899
main='main.c',
99100
test_image='cpmbits/bender_test',
101+
test_dockerfile='test.Dockerfile',
100102
toolchain_prefix='arm-linux-gnueabi-'
101103
)
102104
}

test/e2e/test_cpm.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,17 @@ def test_run_tests_from_docker_image(self):
113113
result = test.execute([])
114114
assert result.status_code == 0
115115

116+
def test_run_tests_using_dockerfile(self):
117+
os.chdir(self.PROJECT_DIRECTORY)
118+
self.add_bit('test', 'cest', '1.0')
119+
self.set_target_image('default', 'cpmbits/ubuntu:20.04')
120+
self.set_target_test_dockerfile('default', f'../environment/Dockerfile')
121+
self.set_test_ldflags(['-Wl,-s'])
122+
self.add_test('test_case.cpp')
123+
install.execute(['-s', 'http://localhost:8000'])
124+
result = test.execute([])
125+
assert result.status_code == 0
126+
116127
def test_build_from_docker_image_with_non_default_target(self):
117128
os.chdir(self.PROJECT_DIRECTORY)
118129
self.set_target_image('ubuntu', 'cpmbits/ubuntu:20.04')
@@ -268,6 +279,11 @@ def set_target_dockerfile(self, target_name, dockerfile):
268279
lambda descriptor: descriptor.setdefault('targets', {}).setdefault(target_name, {}).update({'dockerfile': dockerfile})
269280
)
270281

282+
def set_target_test_dockerfile(self, target_name, dockerfile):
283+
self.modify_descriptor(
284+
lambda descriptor: descriptor.setdefault('targets', {}).setdefault(target_name, {}).update({'test_dockerfile': dockerfile})
285+
)
286+
271287
def set_target_ldflags(self, target_name, ldflags):
272288
self.modify_descriptor(
273289
lambda descriptor: descriptor.setdefault('targets', {}).setdefault(target_name, {}).update({'ldflags': ldflags})

0 commit comments

Comments
 (0)