diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/cases/abs.ll | 22 | ||||
-rw-r--r-- | tests/cases/caall.ll | 25 | ||||
-rw-r--r-- | tests/cases/entry2.ll | 32 | ||||
-rw-r--r-- | tests/cases/entry2.txt | 2 | ||||
-rw-r--r-- | tests/gl_renderers.c | 191 | ||||
-rw-r--r-- | tests/gl_renderers.png | bin | 0 -> 345620 bytes | |||
-rwxr-xr-x | tests/runner.py | 198 | ||||
-rw-r--r-- | tests/websockets_select.c | 95 | ||||
-rw-r--r-- | tests/websockets_select_server_closes_connection.c | 126 | ||||
-rw-r--r-- | tests/websockets_select_server_closes_connection_rw.c | 213 |
10 files changed, 892 insertions, 12 deletions
diff --git a/tests/cases/abs.ll b/tests/cases/abs.ll new file mode 100644 index 00000000..57e06928 --- /dev/null +++ b/tests/cases/abs.ll @@ -0,0 +1,22 @@ +; ModuleID = 'tests/hello_world.bc' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] + +; [#uses=0] +define i32 @main() { +entry: + %retval = alloca i32, align 4 ; [#uses=1 type=i32*] + store i32 0, i32* %retval + %zero = zext i8 0 to i32 + %a = call i32 (i32)* @abs(i32 %zero) + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0), i32 %a, i32 %zero) ; [#uses=0 type=i32] + ret i32 1 +} + +; [#uses=1] +declare i32 @printf(i8*, ...) + +declare i32 @abs(i32) + diff --git a/tests/cases/caall.ll b/tests/cases/caall.ll new file mode 100644 index 00000000..313116e6 --- /dev/null +++ b/tests/cases/caall.ll @@ -0,0 +1,25 @@ +; ModuleID = 'tests/hello_world.bc' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] + +; [#uses=0] +define i32 @main() { +entry: + %retval = alloca i32, align 4 ; [#uses=1 type=i32*] + store i32 0, i32* %retval + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32] + %call12 = call void (i32*)** @_ZNSt3__13mapINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPFvP6ObjectENS_4lessIS6_EENS4_INS_4pairIKS6_SA_EEEEEixERSE_(i32 10) + %26 = load void (%class.Object*)** %call12 + ret i32 1 +} + +define (i32*)** @_ZNSt3__13mapINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPFvP6ObjectENS_4lessIS6_EENS4_INS_4pairIKS6_SA_EEEEEixERSE_(i32 %x) { +entry: + %ret = inttoptr i32 0 to (i32*)** + ret %ret +} + +; [#uses=1] +declare i32 @printf(i8*, ...) diff --git a/tests/cases/entry2.ll b/tests/cases/entry2.ll new file mode 100644 index 00000000..75b266c7 --- /dev/null +++ b/tests/cases/entry2.ll @@ -0,0 +1,32 @@ +; ModuleID = '/tmp/tmpKnA2D3/a.out.bc' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@.str = private unnamed_addr constant [11 x i8] c"getgid=%d\0A\00", align 1 +@.str1 = private unnamed_addr constant [6 x i8] c"f=%d\0A\00", align 1 + +define internal i32 @_Z1fii(i32, i32) noinline { + %3 = tail call i32 @getgid() + %4 = icmp eq i32 %3, 0 + br i1 %4, label %7, label %5 + +; <label>:5 ; preds = %2 + %6 = tail call i32 @getgid() + br label %7 + +; <label>:7 ; preds = %5, %2 + %.0 = phi i32 [ 0, %5 ], [ 1, %2 ] + ret i32 %.0 +} + +declare i32 @getgid() + +define i32 @main() { + %1 = tail call i32 @getgid() + %2 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str, i32 0, i32 0), i32 %1) + %3 = tail call i32 @_Z1fii(i32 undef, i32 undef) + %4 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str1, i32 0, i32 0), i32 %3) + ret i32 0 +} + +declare i32 @printf(i8* nocapture, ...) nounwind diff --git a/tests/cases/entry2.txt b/tests/cases/entry2.txt new file mode 100644 index 00000000..37642b36 --- /dev/null +++ b/tests/cases/entry2.txt @@ -0,0 +1,2 @@ +getgid=0 +f=1 diff --git a/tests/gl_renderers.c b/tests/gl_renderers.c new file mode 100644 index 00000000..0a8e6e78 --- /dev/null +++ b/tests/gl_renderers.c @@ -0,0 +1,191 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#if !EMSCRIPTEN +#define USE_GLEW 0 +#endif + +#if USE_GLEW +#include "GL/glew.h" +#endif + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#if !USE_GLEW +#include "SDL/SDL_opengl.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + typedef struct Vertex { + GLfloat x; + GLfloat y; + } Vertex; + + typedef struct Color { + GLubyte r; + GLubyte g; + GLubyte b; + GLubyte a; + } Color; + + typedef struct Type1 { + Vertex location; + Color color; + } Type1; + + typedef struct Type2 { + GLuint unused1; + Vertex location; + GLfloat unused2; + Color color; + } Type2; + + Type1 first[3] = { + {{-1.0, 0.0}, {0xFF, 0x00, 0x00, 0xFF}}, + {{ 0.0, 1.0}, {0x00, 0xFF, 0x00, 0xFF}}, + {{ 1.0, 0.0}, {0x00, 0x00, 0xFF, 0xFF}} + }; + + Type2 second[3] = { + {0.0, {-1.0, 0.0}, 0.0, {0xFF, 0x00, 0x00, 0xFF}}, + {0.0, { 1.0, 0.0}, 0.0, {0x00, 0x00, 0xFF, 0xFF}}, + {0.0, { 0.0, -1.0}, 0.0, {0x00, 0xFF, 0x00, 0xFF}}}; + + // make two vbo objects + GLuint vbo[2]; + glGenBuffers(2, &vbo[0]); + + // load the first into the context + glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); + + // allocate enough space for 100 vertices + glBufferData(GL_ARRAY_BUFFER, sizeof(Type1)*100, NULL, GL_DYNAMIC_DRAW); + + // load the second into the context + glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); + + // allocate enough space for 100 vertices + glBufferData(GL_ARRAY_BUFFER, sizeof(Type2)*100, NULL, GL_DYNAMIC_DRAW); + + // DRAW + + GLbyte * pointer; + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + // FIRST + // load the first into the context + glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); + + // Load actual data in + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Type1)*3, &first[0]); + + // point to the buffer's location data + glVertexPointer(2, GL_FLOAT, sizeof(Type1), NULL); + + pointer = (GLbyte*)(((GLbyte*)&first[0].color) - ((GLbyte*)&first[0].location)); + + printf("location = %p\n", pointer); + // point to the buffer's color data + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Type1), pointer); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + + // SECOND + + // load the first into the context + glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); + + // Load actual data in + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Type2)*3, &second[0]); + + pointer = (GLbyte*)((GLbyte*)&second[0].location - (GLbyte*)&second[0].unused1); + + // point to the buffer's location data + printf("location = %p\n", pointer); + glVertexPointer(2, GL_FLOAT, sizeof(Type2), pointer); + + pointer = (GLbyte*)((GLbyte*)&second[0].color - (GLbyte*)&second[0].unused1); + + // point to the buffer's location data + printf("location = %p\n", pointer); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Type2), pointer); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(3000); +#endif + + SDL_Quit(); + + return 0; +} diff --git a/tests/gl_renderers.png b/tests/gl_renderers.png Binary files differnew file mode 100644 index 00000000..db1bc751 --- /dev/null +++ b/tests/gl_renderers.png diff --git a/tests/runner.py b/tests/runner.py index f0b5445c..ef014a18 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -1635,7 +1635,11 @@ Succeeded! def test_floatvars(self): src = ''' #include <stdio.h> - #include <math.h> + + // headers test, see issue #1013 + #include<cfloat> + #include<cmath> + int main(int argc, char **argv) { float x = 1.234, y = 3.5, q = 0.00000001; @@ -2475,17 +2479,13 @@ Exception execution path of first function! 1 def test_exceptions(self): if Settings.ASM_JS: return self.skip('no exceptions support in asm') if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1") + if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') Settings.EXCEPTION_DEBUG = 1 - self.banned_js_engines = [NODE_JS] # node issue 1669, exception causes stdout not to be flushed Settings.DISABLE_EXCEPTION_CATCHING = 0 - if self.emcc_args is None: - if Building.LLVM_OPTS: return self.skip('optimizing bitcode before emcc can confuse libcxx inclusion') - self.emcc_args = [] # libc++ auto-inclusion is only done if we use emcc - else: - if '-O2' in self.emcc_args: - self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage + if '-O2' in self.emcc_args: + self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage src = ''' #include <stdio.h> @@ -6245,6 +6245,36 @@ def process(filename): } ''' self.do_run(src, "some string constant") + + def test_std_cout_new(self): + if self.emcc_args is None: return self.skip('requires emcc') + + src = ''' + #include <iostream> + + struct NodeInfo { //structure that we want to transmit to our shaders + float x; + float y; + float s; + float c; + }; + const int nbNodes = 100; + NodeInfo * data = new NodeInfo[nbNodes]; //our data that will be transmitted using float texture. + + template<int i> + void printText( const char (&text)[ i ] ) + { + std::cout << text << std::endl; + } + + int main() + { + printText( "some string constant" ); + return 0; + } + ''' + + self.do_run(src, "some string constant") def test_istream(self): if self.emcc_args is None: return self.skip('requires libcxx') @@ -7193,7 +7223,6 @@ void*:16 def test_mmap(self): if self.emcc_args is None: return self.skip('requires emcc') - self.banned_js_engines = [NODE_JS] # slower, and fail on 64-bit Settings.TOTAL_MEMORY = 128*1024*1024 @@ -7642,7 +7671,6 @@ def process(filename): try: os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1' - #self.banned_js_engines = [NODE_JS] # node issue 1669, exception causes stdout not to be flushed Settings.CHECK_OVERFLOWS = 0 for name in glob.glob(path_from_root('tests', 'cases', '*.ll')): @@ -10071,8 +10099,8 @@ f.close() try: os.environ['EMCC_DEBUG'] = '1' for asm, linkable, chunks, js_chunks in [ - (0, 0, 3, 2), (0, 1, 3, 4), - (1, 0, 3, 2), (1, 1, 3, 4) + (0, 0, 3, 2), (0, 1, 4, 4), + (1, 0, 3, 2), (1, 1, 4, 4) ]: print asm, linkable, chunks, js_chunks output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O1', '-s', 'LINKABLE=%d' % linkable, '-s', 'ASM_JS=%d' % asm, '-s', 'UNRESOLVED_AS_DEAD=1'] + (['-O2'] if asm else []), stdout=PIPE, stderr=PIPE).communicate() @@ -11040,6 +11068,84 @@ elif 'browser' in str(sys.argv): Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'page.html', '--pre-js', 'pre.js']).communicate() self.run_browser('page.html', '', '/report_result?740') + def test_sdl_mouse_offsets(self): + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + function simulateMouseEvent(x, y, button) { + var event = document.createEvent("MouseEvents"); + if (button >= 0) { + var event1 = document.createEvent("MouseEvents"); + event1.initMouseEvent('mousedown', true, true, window, + 1, x, y, x, y, + 0, 0, 0, 0, + button, null); + Module['canvas'].dispatchEvent(event1); + var event2 = document.createEvent("MouseEvents"); + event2.initMouseEvent('mouseup', true, true, window, + 1, x, y, x, y, + 0, 0, 0, 0, + button, null); + Module['canvas'].dispatchEvent(event2); + } else { + var event1 = document.createEvent("MouseEvents"); + event1.initMouseEvent('mousemove', true, true, window, + 0, x, y, x, y, + 0, 0, 0, 0, + 0, null); + Module['canvas'].dispatchEvent(event1); + } + } + window['simulateMouseEvent'] = simulateMouseEvent; + ''') + open(os.path.join(self.get_dir(), 'page.html'), 'w').write(''' + <html> + <head> + <style type="text/css"> + html, body { margin: 0; padding: 0; } + #container { + position: absolute; + left: 5px; right: 0; + top: 5px; bottom: 0; + } + #canvas { + position: absolute; + left: 0; width: 600px; + top: 0; height: 450px; + } + textarea { + margin-top: 500px; + margin-left: 5px; + width: 600px; + } + </style> + </head> + <body> + <div id="container"> + <canvas id="canvas"></canvas> + </div> + <textarea id="output" rows="8"></textarea> + <script type="text/javascript"> + var Module = { + canvas: document.getElementById('canvas'), + print: (function() { + var element = document.getElementById('output'); + element.value = ''; // clear browser cache + return function(text) { + text = Array.prototype.slice.call(arguments).join(' '); + element.value += text + "\\n"; + element.scrollTop = 99999; // focus on bottom + }; + })() + }; + </script> + <script type="text/javascript" src="sdl_mouse.js"></script> + </body> + </html> + ''') + open(os.path.join(self.get_dir(), 'sdl_mouse.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_mouse.c')).read())) + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'sdl_mouse.js', '--pre-js', 'pre.js']).communicate() + self.run_browser('page.html', '', '/report_result?600') + def test_sdl_audio(self): shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) shutil.copyfile(path_from_root('tests', 'sounds', 'alarmcreatemiltaryfoot_1.wav'), os.path.join(self.get_dir(), 'sound2.wav')) @@ -11425,6 +11531,9 @@ elif 'browser' in str(sys.argv): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('gl_ps_strides.c', reference='gl_ps_strides.png', args=['--preload-file', 'screenshot.png']) + def test_gl_renderers(self): + self.btest('gl_renderers.c', reference='gl_renderers.png', args=['-s', 'GL_UNSAFE_OPTS=0']) + def test_matrix_identity(self): self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840']) @@ -11757,6 +11866,71 @@ elif 'browser' in str(sys.argv): finally: self.clean_pids() + def test_websockets_select_server_down(self): + def closedServer(q): + import socket + + q.put(None) # No sub-process to start + ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ssock.bind(("127.0.0.1", 8994)) + try: + with self.WebsockHarness(8994, closedServer): + self.btest('websockets_select.c', expected='266') + finally: + self.clean_pids() + + def test_websockets_select_server_closes_connection(self): + def closingServer(q): + import socket + + q.put(None) # No sub-process to start + ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ssock.bind(("127.0.0.1", 8994)) + ssock.listen(2) + while True: + csock, addr = ssock.accept() + print "Connection from %s" % repr(addr) + csock.send("1234567") + csock.close() + + try: + with self.WebsockHarness(8994, closingServer): + self.btest('websockets_select_server_closes_connection.c', expected='266') + finally: + self.clean_pids() + + def test_websockets_select_server_closes_connection_rw(self): + def closingServer_rw(q): + import socket + + q.put(None) # No sub-process to start + ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ssock.bind(("127.0.0.1", 8998)) + ssock.listen(2) + while True: + csock, addr = ssock.accept() + print "Connection from %s" % repr(addr) + readArray = bytearray(10) + #readBuffer = buffer(readArray) + bytesRead = 0 + # Let the client start to write data + while (bytesRead < 10): + (readBytes, address) = csock.recvfrom_into( readArray, 10 ) + bytesRead += readBytes + print "server: 10 bytes read" + # Now we write a message on our own ... + csock.send("0123456789") + print "server: 10 bytes written" + # And immediately close the connection + csock.close() + print "server: connection closed" + + try: + with self.WebsockHarness(8998, closingServer_rw): + self.btest('websockets_select_server_closes_connection_rw.c', expected='266') + finally: + self.clean_pids() + def test_enet(self): try_delete(self.in_dir('enet')) shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet')) diff --git a/tests/websockets_select.c b/tests/websockets_select.c new file mode 100644 index 00000000..b8ab9091 --- /dev/null +++ b/tests/websockets_select.c @@ -0,0 +1,95 @@ +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <assert.h> +#if EMSCRIPTEN +#include <emscripten.h> +#endif + +#define EXPECTED_BYTES 5 + +int SocketFD; + +int done = 0; + +void iter(void *arg) { + fd_set sett; + FD_ZERO(&sett); + FD_SET(SocketFD, &sett); + + // The error should happen here + int select_says_yes = select(64, &sett, NULL, NULL, NULL); + if( select_says_yes == -1 ){ + printf( "Connection to websocket server failed as expected." ); + perror( "Error message" ); + int result = 266; + REPORT_RESULT(); + done = 1; + } + + assert(!select_says_yes); + done = 1; +} + +// This is for testing a websocket connection to a closed server port. +// The connect call will succeed (due to the asynchronous websocket +// behavior) but once the underlying websocket system realized that +// the connection cannot be established, the next select call will fail. +int main(void) +{ + struct sockaddr_in stSockAddr; + int Res; + SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (-1 == SocketFD) + { + perror("cannot create socket"); + exit(EXIT_FAILURE); + } + + memset(&stSockAddr, 0, sizeof(stSockAddr)); + + stSockAddr.sin_family = AF_INET; + stSockAddr.sin_port = htons( +#if EMSCRIPTEN + 8995 +#else + 8994 +#endif + ); + Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); + + if (0 > Res) { + perror("error: first parameter is not a valid address family"); + close(SocketFD); + exit(EXIT_FAILURE); + } else if (0 == Res) { + perror("char string (second parameter does not contain valid ipaddress)"); + close(SocketFD); + exit(EXIT_FAILURE); + } + + // This call should succeed (even if the server port is closed) + if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + perror("connect failed"); + close(SocketFD); + exit(EXIT_FAILURE); + + } + +#if EMSCRIPTEN + emscripten_set_main_loop(iter, 0, 0); +#else + while (!done) iter(NULL); +#endif + + return EXIT_SUCCESS; +} + diff --git a/tests/websockets_select_server_closes_connection.c b/tests/websockets_select_server_closes_connection.c new file mode 100644 index 00000000..6ce6d311 --- /dev/null +++ b/tests/websockets_select_server_closes_connection.c @@ -0,0 +1,126 @@ +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <assert.h> +#if EMSCRIPTEN +#include <emscripten.h> +#endif + +#define EXPECTED_BYTES 5 + +int SocketFD; + +int done = 0; + +void iter(void *arg) { + static char readbuf[1024]; + static int readPos = 0; + + fd_set sett; + FD_ZERO(&sett); + FD_SET(SocketFD, &sett); + + if( readPos < 7 ){ + // still reading + int selectRes = select(64, &sett, NULL, NULL, NULL); + + if( selectRes == 0 ) + return; + + if( selectRes == -1 ){ + perror( "Connection to websocket server failed" ); + exit(EXIT_FAILURE); + } + if( selectRes > 0 ){ + assert(FD_ISSET(SocketFD, &sett)); + + int bytesRead = recv( SocketFD, readbuf+readPos, 7-readPos, 0 ); + readPos += bytesRead; + } + } else { + // here the server should have closed the connection + int selectRes = select(64, &sett, NULL, NULL, NULL); + + if( selectRes == 0 ) + return; + + if( selectRes == -1 ){ + perror( "Connection to websocket server failed as expected" ); + int result = 266; + REPORT_RESULT(); + emscripten_cancel_main_loop(); + done = 1; + } + + if( selectRes > 0 ){ + printf( "Error: socket should not show up on select call anymore.\n" ); + exit(EXIT_FAILURE); + } + } + + return; +} + +// Scenario: the server sends data and closes the connection after 7 bytes. +// This test should provoke the situation in which the underlying +// tcp connection has been torn down already but there is still data +// in the inQueue. The select call has to succeed as long the queue +// still contains data and only then start to throw errors. +int main(void) +{ + struct sockaddr_in stSockAddr; + int Res; + SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (-1 == SocketFD) + { + perror("cannot create socket"); + exit(EXIT_FAILURE); + } + + memset(&stSockAddr, 0, sizeof(stSockAddr)); + + stSockAddr.sin_family = AF_INET; + stSockAddr.sin_port = htons( +#if EMSCRIPTEN + 8995 +#else + 8994 +#endif + ); + Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); + + if (0 > Res) { + perror("error: first parameter is not a valid address family"); + close(SocketFD); + exit(EXIT_FAILURE); + } else if (0 == Res) { + perror("char string (second parameter does not contain valid ipaddress)"); + close(SocketFD); + exit(EXIT_FAILURE); + } + + // This call should succeed (even if the server port is closed) + if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + perror("connect failed"); + close(SocketFD); + exit(EXIT_FAILURE); + + } + +#if EMSCRIPTEN + emscripten_set_main_loop(iter, 0, 0); +#else + while (!done) iter(NULL); +#endif + + return EXIT_SUCCESS; +} + diff --git a/tests/websockets_select_server_closes_connection_rw.c b/tests/websockets_select_server_closes_connection_rw.c new file mode 100644 index 00000000..dd0913bf --- /dev/null +++ b/tests/websockets_select_server_closes_connection_rw.c @@ -0,0 +1,213 @@ +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <assert.h> +#if EMSCRIPTEN +#include <emscripten.h> +#endif + +#define EXPECTED_BYTES 5 + +int SocketFD; + +int done = 0; + +void iter(void *arg) { + static int state = 0; + static char writebuf[] = "01234567890123456789"; + static int writePos = 0; + static char readbuf[1024]; + static int readPos = 0; + int selectRes; + ssize_t transferAmount; + fd_set sett; + + + switch( state ){ + case 0: + // writing 10 bytes to the server + + // the socket in the read file descriptors has to result in a 0 return value + // because the connection exists, but there is no data yet + FD_ZERO( &sett ); + FD_SET(SocketFD, &sett); + selectRes = select(64, &sett, NULL, NULL, NULL); + if( selectRes != 0 ){ + printf( "case 0: read select != 0\n" ); + exit(EXIT_FAILURE); + } + + // the socket in the write file descriptors has to result in either a 0 or 1 + // the connection either is setting up or is established and writing is possible + FD_ZERO( &sett ); + FD_SET(SocketFD, &sett); + selectRes = select(64, NULL, &sett, NULL, NULL); + if( selectRes == -1 ){ + printf( "case 0: write select == -1\n" ); + exit(EXIT_FAILURE); + } + if( selectRes == 0 ){ + return; + } + + // send a single byte + transferAmount = send( SocketFD, writebuf+writePos, 1, 0 ); + writePos += transferAmount; + + // after 10 bytes switch to next state + if( writePos >= 10 ){ + state = 1; + } + break; + + case 1: + // wait until we can read one byte to make sure the server + // has sent the data and then closed the connection + FD_ZERO( &sett ); + FD_SET(SocketFD, &sett); + selectRes = select(64, &sett, NULL, NULL, NULL); + if( selectRes == -1 ){ + printf( "case 1: read selectRes == -1\n" ); + exit(EXIT_FAILURE); + } + if( selectRes == 0 ) + return; + + // read a single byte + transferAmount = recv( SocketFD, readbuf+readPos, 1, 0 ); + readPos += transferAmount; + + // if successfully reading 1 byte, switch to next state + if( readPos >= 1 ){ + state = 2; + } + break; + + case 2: + // calling select with the socket in the write file descriptors has + // to fail because the tcp network connection is already down + FD_ZERO( &sett ); + FD_SET(SocketFD, &sett); + selectRes = select(64, NULL, &sett, NULL, NULL); + if( selectRes != -1 ){ + printf( "case 2: write selectRes != -1\n" ); + exit(EXIT_FAILURE); + } + + // calling select with the socket in the read file descriptors + // has to succeed because there is still data in the inQueue + FD_ZERO( &sett ); + FD_SET(SocketFD, &sett); + selectRes = select(64, &sett, NULL, NULL, NULL); + if( selectRes != 1 ){ + printf( "case 2: read selectRes != 1\n" ); + exit(EXIT_FAILURE); + } + if( selectRes == 0 ) + return; + + // read a single byte + transferAmount = recv( SocketFD, readbuf+readPos, 1, 0 ); + readPos += transferAmount; + + // with 10 bytes read the inQueue is empty => switch state + if( readPos >= 10 ){ + state = 3; + } + break; + + case 3: + // calling select with the socket in the read file descriptors + // now also has to fail as the inQueue is empty + FD_ZERO( &sett ); + FD_SET(SocketFD, &sett); + selectRes = select(64, &sett, NULL, NULL, NULL); + if( selectRes != -1 ){ + printf( "case 3: read selectRes != -1\n" ); + exit(EXIT_FAILURE); + } + + // report back success, the 266 is just an arbitrary value without + // deeper meaning + int result = 266; + REPORT_RESULT(); + break; + + default: + printf( "Impossible state!\n" ); + exit(EXIT_FAILURE); + break; + } + + return; +} + +// This test checks for an intended asymmetry in the behavior of the select function. +// Scenario: the client sends data to the server. After 10 received bytes the +// server sends 10 bytes on its own and immediately afterwards closes the connection. +// This mimics a typical connect-request-response-disconnect situation. +// After the server closed the connection select calls with the socket in the write file +// descriptors have to fail as the tcp connection is already down and there is no way +// anymore to send data. +// Select calls with the socket in the read file descriptor list still have to succeed +// as there are still 10 bytes to read from the inQueue. So, for the same socket the +// select call behaves differently depending on whether the socket is listed in the +// read or write file descriptors. +int main(void) +{ + struct sockaddr_in stSockAddr; + int Res; + SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (-1 == SocketFD) + { + perror("cannot create socket"); + exit(EXIT_FAILURE); + } + + memset(&stSockAddr, 0, sizeof(stSockAddr)); + + stSockAddr.sin_family = AF_INET; + stSockAddr.sin_port = htons( +#if EMSCRIPTEN + 8999 +#else + 8998 +#endif + ); + Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); + + if (0 > Res) { + perror("error: first parameter is not a valid address family"); + close(SocketFD); + exit(EXIT_FAILURE); + } else if (0 == Res) { + perror("char string (second parameter does not contain valid ipaddress)"); + close(SocketFD); + exit(EXIT_FAILURE); + } + + // This call should succeed (even if the server port is closed) + if (-1 == connect(SocketFD, (struct sockaddr |