package com.seaofnodes.simple; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen; import com.seaofnodes.simple.type.TypeInteger; import com.seaofnodes.simple.util.Ary; import java.nio.file.Files; import java.nio.file.Path; import java.util.concurrent.TimeUnit; import org.junit.Test; import static org.junit.Assert.*; public class StreamIOTest extends TestC { @Test public void testReadUntilEOF() throws Exception { String src=""" val main={ -> val bytes=sys.io.stdin(); if(!bytes) return 1; return sys.io.write(1,bytes)==bytes# ? 0 : 2; }; """; String exe=compile(src,"streamEcho",new Ary<>(new String[]{RELEASE_SYS_DIR}), CALL_CONVENTION,null,null,-1); for(int size:new int[]{0,8192,8193,65537}) { String input="a\0bc\n".repeat(size/5)+"z".repeat(size%5); Path in=Files.createTempFile(Path.of("build/objs"),"stream-in-",".txt"); Path out=Files.createTempFile(Path.of("build/objs"),"stream-out-",".txt"); Process child=null; try { Files.writeString(in,input); ProcessBuilder builder=new ProcessBuilder(exe).redirectOutput(out.toFile()); if(size!=8193) builder.redirectInput(in.toFile()); child=builder.start(); if(size==8193) { // Also exercise a real pipe, ending only when its writer closes. child.getOutputStream().write(input.getBytes(java.nio.charset.StandardCharsets.UTF_8)); child.getOutputStream().close(); } assertTrue(child.waitFor(TEST_TIMEOUT_SECONDS,TimeUnit.SECONDS)); assertEquals(0,child.exitValue()); assertEquals(input,Files.readString(out)); } finally { if(child!=null) child.destroyForcibly(); Files.deleteIfExists(in); Files.deleteIfExists(out); } } } @Test public void testUnsignedArrayGrowth() throws Exception { runSYS(""" val main={ -> val b=new sys.aryu8(); val first=b.buf; b.add('a'); if(b.buf!=first || b.len!=1) return 1; b.addAry("bcdefghij"); if(b.len!=10 || b.buf[0]!='a' || b.buf[9]!='j') return 2; b.undo(4294967295); if(b.len!=0) return 3; b.len=2147483648; b.undo(1); if(b.len!=2147483647) return 4; b.clear(); val nums=new sys.aryi64(new i64[0]); nums.add(10).add(20); if(nums.len!=2 || nums.get(0)!=10 || nums.get(1)!=20) return 5; nums.len=4294967295; if(nums.len!=4294967295) return 6; nums.len=2; if(sys.ary.capacity(0,1)!=1) return 7; if(sys.ary.capacity(8,8)!=8 || sys.ary.capacity(8,9)!=16) return 8; if(sys.ary.capacity(2147483648,2147483649)!=4294967295) return 9; if(sys.ary.capacity(4294967295,4294967296)!=0) return 10; if(sys.ary.cmp("abc",4294967295,"",0,4294967295)!=0) return 11; nums.str().add('\\n').write(1); return 0; }; ""","unsignedArrayGrowth","[10, 20]\n",-1); } @Test public void testShortInterruptedAndFailedIO() throws Exception { String src=""" {i32 i64 -> i32} test_io_mode="C"; {i64} test_io_count="C"; {i32} test_io_status="C"; val main={ -> test_io_mode(1,0); val bytes=sys.io.read(700); if(!bytes) return 1; if(bytes#!=24593) return 2; for(u32 i=0; i(new String[]{RELEASE_SYS_DIR}), "streamFaults",src,126,true,TypeInteger.BOT); code.driver(CPU_PORT,CALL_CONVENTION,false,true); String exe="build/objs/streamFaults"+(OS.startsWith("Windows") ? ".exe" : ""); Ary gcc=new Ary<>(String.class); gcc.add("gcc"); gcc.add(OS.startsWith("Windows") ? "-Wl,--image-base,0x400000" : "-no-pie"); gcc.add("-Wl,--wrap=read,--wrap=write"); gcc.add(C_DRIVERS_DIR+"stream_io.c"); gcc.addAll(linkObjs(code)); gcc.add(runtimeObject("simple_crt")); gcc.add(runtimeObject("simple_args")); gcc.addAll(new String[]{"-lm","-o",exe}); exec(TEST_TIMEOUT_SECONDS,gcc.asAry()); assertEquals("ok\n",exec(TEST_TIMEOUT_SECONDS,exe)); } }