Skip to content

Commit

Permalink
Fix Windows line endings and add tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
floitsch committed Oct 25, 2024
1 parent db65f1e commit d7f8a5f
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 2 deletions.
20 changes: 18 additions & 2 deletions src/primitive_core.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ namespace toit {
MODULE_IMPLEMENTATION(core, MODULE_CORE)

#if defined(TOIT_WINDOWS)
#include <windows.h>
#include <vector>

static void replace_line_endings(const uint8_t* bytes, size_t length, std::vector<uint8_t>& buffer) {
for (size_t i = 0; i < length; ++i) {
if (bytes[i] == '\n' && (i == 0 || bytes[i - 1] != '\r')) {
buffer.push_back('\r'); // Add '\r' before '\n' if not already preceded by it.
}
buffer.push_back(bytes[i]);
}
}

static Object* write_on_std(const uint8_t* bytes, size_t length, bool is_stdout, bool newline, Process* process) {
HANDLE console = GetStdHandle(is_stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
if (console == INVALID_HANDLE_VALUE) {
Expand All @@ -82,17 +94,21 @@ static Object* write_on_std(const uint8_t* bytes, size_t length, bool is_stdout,
DWORD written;
DWORD mode;

// Prepare buffer with replaced line endings.
std::vector<uint8_t> buffer;
replace_line_endings(bytes, length, buffer);

// Check if the handle is a console handle.
if (GetConsoleMode(console, &mode)) {
// Write to the console.
WriteConsoleA(console, bytes, (DWORD)length, &written, NULL);
WriteConsoleA(console, buffer.data(), (DWORD)buffer.size(), &written, NULL);

if (newline) {
WriteConsoleA(console, "\r\n", 2, &written, NULL);
}
} else {
// Handle redirection case.
WriteFile(console, bytes, (DWORD)length, &written, NULL);
WriteFile(console, buffer.data(), (DWORD)buffer.size(), &written, NULL);

if (newline) {
WriteFile(console, "\r\n", 2, &written, NULL);
Expand Down
25 changes: 25 additions & 0 deletions tests/null-string-test.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (C) 2024 Toitware ApS.
// Use of this source code is governed by a Zero-Clause BSD license that can
// be found in the tests/LICENSE file.
import expect show *
import host.directory
import host.file

main:
null-string := "foo\0bar"
test: directory.mkdir null-string
test: directory.mkdtemp null-string
test: file.is-directory null-string
test: file.is-file null-string
e := catch: file.Stream.for-read null-string
expect (e.starts-with "INVALID_ARGUMENT")
e = catch: file.Stream.for-write null-string
expect (e.starts-with "INVALID_ARGUMENT")

// However, print works.
print null-string
print-on-stderr_ null-string

test [block]:
expect-throw "INVALID_ARGUMENT" block
65 changes: 65 additions & 0 deletions tests/stdout-stderr-test-compiler.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (C) 2024 Toitware ApS.
// Use of this source code is governed by a Zero-Clause BSD license that can
// be found in the tests/LICENSE file.
import expect show *
import host.pipe
import io
import system

// Marked as compiler test so we get the toit.run path.
main args:
if args.size == 3 and args[0] == "RUN_TEST":
is-stdout := args[1] == "STDOUT"
test-case := int.parse args[2]
run-spawned --is-stdout=is-stdout test-case
return

toit-run := args[0]
run-tests toit-run

TESTS ::= [
"",
"foo",
"foo\nbar",
]

run-spawned test-case/int --is-stdout/bool -> none:
if is-stdout:
print_ TESTS[test-case]
else:
print-on-stderr_ TESTS[test-case]

run args --stdout/bool=false --stderr/bool=false -> string:
pipes := pipe.fork
true // use_path
pipe.PIPE-INHERITED // stdin
stdout ? pipe.PIPE-CREATED : pipe.PIPE-INHERITED // stdout
stderr ? pipe.PIPE-CREATED : pipe.PIPE-INHERITED // stderr
args[0]
args

out-pipe := stdout ? pipes[1] : pipes[2]
pid := pipes[3]

reader := io.Reader.adapt out-pipe
reader.buffer-all
output := reader.read-string (reader.buffered-size)

exit-value := pipe.wait-for pid
exit-code := pipe.exit-code exit-value

if exit-code != 0: throw "Program didn't exit with 0."
return output

run-tests toit-run:
this-file := system.program-path
expect (this-file.ends-with ".toit")
for i := 0; i < TESTS.size; i++:
test/string := TESTS[i]
stdout-output := run [toit-run, this-file, "RUN_TEST", "STDOUT", "$i"] --stdout
stderr-output := run [toit-run, this-file, "RUN_TEST", "STDERR", "$i"] --stderr
expect-equals stdout-output stderr-output

expected := "$test\n".replace --all "\n" system.LINE-TERMINATOR
expect-equals expected stdout-output

0 comments on commit d7f8a5f

Please sign in to comment.