Skip to content

Commit 1c96d90

Browse files
skuznetsov-albaev
andauthored
Support utf8 env properties (#3266)
Co-authored-by: Dmitry Baev <baev.dm@gmail.com>
1 parent 746455f commit 1c96d90

File tree

2 files changed

+120
-20
lines changed

2 files changed

+120
-20
lines changed

allure-generator/src/main/java/io/qameta/allure/allure1/Allure1Plugin.java

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@
4747

4848
import java.io.IOException;
4949
import java.io.InputStream;
50+
import java.io.InputStreamReader;
51+
import java.io.PushbackReader;
5052
import java.math.BigInteger;
53+
import java.nio.charset.CharacterCodingException;
54+
import java.nio.charset.CharsetDecoder;
55+
import java.nio.charset.CodingErrorAction;
5156
import java.nio.file.Files;
5257
import java.nio.file.LinkOption;
5358
import java.nio.file.Path;
@@ -311,7 +316,7 @@ private Step convert(final Path source,
311316
//Copy test status details to each step set the same status
312317
if (Objects.equals(status, testStatus)) {
313318
current.setStatusMessage(message);
314-
current.setStatusMessage(trace);
319+
current.setStatusTrace(trace);
315320
}
316321
return current;
317322
}
@@ -483,17 +488,17 @@ private Stream<TestSuiteResult> jsonFiles(final Path source) {
483488
}
484489

485490
private Optional<TestSuiteResult> readXmlTestSuiteFile(final Path source) {
486-
try (InputStream is = Files.newInputStream(source)) {
487-
return Optional.of(xmlMapper.readValue(is, TestSuiteResult.class));
491+
try (InputStream inputStream = Files.newInputStream(source)) {
492+
return Optional.of(xmlMapper.readValue(inputStream, TestSuiteResult.class));
488493
} catch (IOException e) {
489494
LOGGER.error("Could not read xml result {}", source, e);
490495
}
491496
return Optional.empty();
492497
}
493498

494499
private Optional<TestSuiteResult> readJsonTestSuiteFile(final Path source) {
495-
try (InputStream is = Files.newInputStream(source)) {
496-
return Optional.of(jsonMapper.readValue(is, TestSuiteResult.class));
500+
try (InputStream inputStream = Files.newInputStream(source)) {
501+
return Optional.of(jsonMapper.readValue(inputStream, TestSuiteResult.class));
497502
} catch (IOException e) {
498503
LOGGER.error("Could not read json result {}", source, e);
499504
return Optional.empty();
@@ -537,20 +542,55 @@ private Map<String, String> processEnvironment(final Path directory) {
537542
return environment;
538543
}
539544

545+
private static Properties propertiesToMap(final Map<String, String> target) {
546+
return new Properties() {
547+
@Override
548+
public Object put(final Object key, final Object value) {
549+
return target.put((String) key, (String) value);
550+
}
551+
};
552+
}
553+
554+
private Map<String, String> readEnvironmentPropertiesUtf8(final Path envPropsFile)
555+
throws CharacterCodingException, IOException {
556+
final Map<String, String> utf8Items = new LinkedHashMap<>();
557+
final CharsetDecoder decoder = UTF_8.newDecoder()
558+
.onMalformedInput(CodingErrorAction.REPORT)
559+
.onUnmappableCharacter(CodingErrorAction.REPORT);
560+
561+
try (InputStream envPropsStream = Files.newInputStream(envPropsFile);
562+
InputStreamReader envPropsReader = new InputStreamReader(envPropsStream, decoder);
563+
PushbackReader pushbackReader = new PushbackReader(envPropsReader, 1)) {
564+
565+
final int firstChar = pushbackReader.read();
566+
if (firstChar != -1 && firstChar != '\uFEFF') {
567+
pushbackReader.unread(firstChar);
568+
}
569+
570+
propertiesToMap(utf8Items).load(pushbackReader);
571+
return utf8Items;
572+
}
573+
}
574+
540575
private Map<String, String> processEnvironmentProperties(final Path directory) {
541576
final Path envPropsFile = directory.resolve("environment.properties");
577+
if (!Files.exists(envPropsFile)) {
578+
return new LinkedHashMap<>();
579+
}
580+
try {
581+
return readEnvironmentPropertiesUtf8(envPropsFile);
582+
} catch (CharacterCodingException e) {
583+
LOGGER.error("Failed to read {} as UTF-8, falling back to ISO-8859-1", envPropsFile, e);
584+
} catch (IOException e) {
585+
LOGGER.error("Could not read environment.properties file {}", envPropsFile, e);
586+
return new LinkedHashMap<>();
587+
}
588+
542589
final Map<String, String> items = new LinkedHashMap<>();
543-
if (Files.exists(envPropsFile)) {
544-
try (InputStream is = Files.newInputStream(envPropsFile)) {
545-
new Properties() {
546-
@Override
547-
public Object put(final Object key, final Object value) {
548-
return items.put((String) key, (String) value);
549-
}
550-
}.load(is);
551-
} catch (IOException e) {
552-
LOGGER.error("Could not read environments.properties file " + envPropsFile, e);
553-
}
590+
try (InputStream inputStream = Files.newInputStream(envPropsFile)) {
591+
propertiesToMap(items).load(inputStream);
592+
} catch (IOException e) {
593+
LOGGER.error("Could not read environment.properties file {}", envPropsFile, e);
554594
}
555595
return items;
556596
}
@@ -559,12 +599,15 @@ private Map<String, String> processEnvironmentXml(final Path directory) {
559599
final Path envXmlFile = directory.resolve("environment.xml");
560600
final Map<String, String> items = new LinkedHashMap<>();
561601
if (Files.exists(envXmlFile)) {
562-
try (InputStream fis = Files.newInputStream(envXmlFile)) {
563-
xmlMapper.readValue(fis, ru.yandex.qatools.commons.model.Environment.class).getParameter().forEach(p ->
564-
items.put(p.getKey(), p.getValue())
602+
try (InputStream envXmlInputStream =
603+
Files.newInputStream(envXmlFile)) {
604+
xmlMapper
605+
.readValue(envXmlInputStream, ru.yandex.qatools.commons.model.Environment.class)
606+
.getParameter()
607+
.forEach(p -> items.put(p.getKey(), p.getValue())
565608
);
566609
} catch (Exception e) {
567-
LOGGER.error("Could not read environment.xml file " + envXmlFile.toAbsolutePath(), e);
610+
LOGGER.error("Could not read environment.xml file {}", envXmlFile.toAbsolutePath(), e);
568611
}
569612
}
570613
return items;

allure-generator/src/test/java/io/qameta/allure/allure1/Allure1PluginTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,18 @@
3535

3636
import java.io.IOException;
3737
import java.io.InputStream;
38+
import java.nio.charset.StandardCharsets;
3839
import java.nio.file.Files;
3940
import java.nio.file.Path;
4041
import java.util.Arrays;
4142
import java.util.Iterator;
43+
import java.util.LinkedHashMap;
4244
import java.util.List;
4345
import java.util.Map;
4446
import java.util.Objects;
4547
import java.util.Optional;
4648
import java.util.Set;
49+
import java.util.function.Supplier;
4750
import java.util.stream.Collectors;
4851
import java.util.stream.Stream;
4952

@@ -355,6 +358,56 @@ void shouldPreserveContentTypeFromAttachment() throws IOException {
355358
assertThat(attachments.get(0).getSource()).endsWith(".txt");
356359
}
357360

361+
@SuppressWarnings("unchecked")
362+
@Test
363+
void shouldReadEnvironmentPropertiesUtf8() throws Exception {
364+
writeBytes(
365+
"test_executor=测试人员 A\n".getBytes(StandardCharsets.UTF_8));
366+
367+
final LaunchResults launchResults = process();
368+
final Map<String, String> env = launchResults.getExtra(
369+
Allure1Plugin.ENVIRONMENT_BLOCK_NAME,
370+
(Supplier<Map<String, String>>) LinkedHashMap::new
371+
);
372+
373+
assertThat(env).containsEntry("test_executor", "测试人员 A");
374+
}
375+
376+
@SuppressWarnings("unchecked")
377+
@Test
378+
void shouldReadEnvironmentPropertiesUtf8WithBom() throws Exception {
379+
final byte[] bom = new byte[] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};
380+
final byte[] content = "executor=测试人员 A\n".getBytes(StandardCharsets.UTF_8);
381+
final byte[] bytes = new byte[bom.length + content.length];
382+
System.arraycopy(bom, 0, bytes, 0, bom.length);
383+
System.arraycopy(content, 0, bytes, bom.length, content.length);
384+
385+
writeBytes(bytes);
386+
387+
final LaunchResults launchResults = process();
388+
final Map<String, String> env = launchResults.getExtra(
389+
Allure1Plugin.ENVIRONMENT_BLOCK_NAME,
390+
(Supplier<Map<String, String>>) LinkedHashMap::new
391+
);
392+
393+
assertThat(env).containsEntry("executor", "测试人员 A");
394+
assertThat(env).doesNotContainKey("\uFEFFexecutor");
395+
}
396+
397+
@SuppressWarnings("unchecked")
398+
@Test
399+
void shouldReturnEmptyMapWhenUtf8DecodingFails() throws Exception {
400+
writeBytes(
401+
"name=café\n".getBytes(StandardCharsets.ISO_8859_1));
402+
403+
final LaunchResults launchResults = process();
404+
final Map<String, String> env = launchResults.getExtra(
405+
Allure1Plugin.ENVIRONMENT_BLOCK_NAME,
406+
(Supplier<Map<String, String>>) LinkedHashMap::new
407+
);
408+
409+
assertThat(env).isEmpty();
410+
}
358411

359412
@Test
360413
void shouldNotAllowInvalidCharactersInAttachmentSource() throws IOException {
@@ -424,6 +477,10 @@ private LaunchResults process(String... strings) throws IOException {
424477
return resultsVisitor.getLaunchResults();
425478
}
426479

480+
private void writeBytes(final byte[] bytes) throws IOException {
481+
Files.write(directory.resolve("environment.properties"), bytes);
482+
}
483+
427484
private void copyFile(Path dir, String resourceName, String fileName) throws IOException {
428485
try (InputStream is = getClass().getClassLoader().getResourceAsStream(resourceName)) {
429486
Files.copy(

0 commit comments

Comments
 (0)