mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-31 00:30:53 +00:00
Initial revision
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
commit
0098375b73
248 changed files with 40027 additions and 0 deletions
149
Testing/Makefile.in
Normal file
149
Testing/Makefile.in
Normal file
|
@ -0,0 +1,149 @@
|
|||
#
|
||||
# Tests makefile for Objective-C Class Library
|
||||
# Copyright (C) 1993 Free Software Foundation, Inc.
|
||||
#
|
||||
# Written by: R. Andrew McCallum <mccallum@cs.rochester.edu>
|
||||
# Dept. of Computer Science, U. of Rochester, Rochester, NY 14627
|
||||
#
|
||||
# This file is part of the GNU Objective-C Class Library.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Library General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Library General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Library General Public
|
||||
# License along with this library; if not, write to the Free
|
||||
# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
#### Start of system configuration section. ####
|
||||
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
CC = @CC@
|
||||
|
||||
CFLAGS = -Wall -Wno-implicit -g -O
|
||||
CPPFLAGS =
|
||||
LDFLAGS =
|
||||
|
||||
DEFS = @DEFS@
|
||||
LIBS = -L$(srcdir)/.. -lobjects @LIBOBJC@ @LIBS@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
NEXT_NEXT_INCLUDES = -I/usr/include
|
||||
OBJECTS_NEXT_INCLUDES = -I$(srcdir)/../objects/next-include
|
||||
NEXT_INCLUDES = @NEXT_INCLUDES@
|
||||
|
||||
ALL_CPPFLAGS = -I$(srcdir)/.. $(NEXT_INCLUDES) $(CPPFLAGS)
|
||||
ALL_CFLAGS = $(CFLAGS)
|
||||
ALL_OBJCFLAGS = $(CFLAGS) -Wno-protocol
|
||||
ALL_LDFLAGS = $(LDFLAGS) $(LIBS)
|
||||
|
||||
.SUFFIXES: .m
|
||||
.m.o:
|
||||
$(CC) -c $(ALL_CPPFLAGS) $(DEFS) $(ALL_OBJCFLAGS) $< -o $*.o
|
||||
.c.o:
|
||||
$(CC) -c $(ALL_CPPFLAGS) $(DEFS) $(ALL_CFLAGS) $< -o $*.o
|
||||
|
||||
SRCS = \
|
||||
test01.m \
|
||||
test02.m \
|
||||
test03.m \
|
||||
test04.m \
|
||||
test05.m \
|
||||
test06.m \
|
||||
test07.m \
|
||||
test08.m \
|
||||
test09.m \
|
||||
test10.m \
|
||||
test11.m \
|
||||
test12.m \
|
||||
test13.m \
|
||||
pipes.m \
|
||||
server.m \
|
||||
client.m
|
||||
|
||||
HDRS = \
|
||||
server.h
|
||||
|
||||
EXCS = $(SRCS:.m=)
|
||||
|
||||
DISTFILES = $(SRCS) $(HDRS) Makefile.in NXStringTable.example
|
||||
|
||||
all: $(EXCS)
|
||||
|
||||
# This works for GNU make, but not others.
|
||||
# %: %.o $(srcdir)/../libobjects.a
|
||||
# $(CC) $(ALL_CFLAGS) $< -o $@ $(ALL_LDFLAGS)
|
||||
# How can I do this in a better way than the ugliness below?
|
||||
# (but also have it work on old-style /bin/make)
|
||||
|
||||
LINK_CMD = $(CC) $(ALL_CFLAGS) $@.o -o $@ $(ALL_LDFLAGS)
|
||||
test01: test01.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test02: test02.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test03: test03.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test04: test04.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test05: test05.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test06: test06.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test07: test07.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test08: test08.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test09: test09.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test10: test10.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test11: test11.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test12: test12.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
test13: test13.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
pipes: pipes.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
server: server.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
client: client.o $(srcdir)/../libobjects.a
|
||||
$(LINK_CMD)
|
||||
|
||||
echo-excs:
|
||||
@echo $(EXCS)
|
||||
|
||||
remote: server client
|
||||
|
||||
mostlyclean:
|
||||
rm -f core *~ test08.data textcoder.txt
|
||||
|
||||
clean: mostlyclean
|
||||
rm -f *.o $(EXCS)
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile config.status
|
||||
|
||||
realclean: distclean
|
||||
rm -f TAGS
|
||||
|
||||
dist-dir: $(srcdir)/../.fname
|
||||
@echo Run make dist from the parent directory.
|
||||
|
||||
dist: $(DISTFILES) dist-dir
|
||||
mkdir $(srcdir)/../`cat $(srcdir)/../.fname`/checks
|
||||
ln $(DISTFILES) $(srcdir)/../`cat $(srcdir)/../.fname`/checks
|
||||
|
||||
Makefile: Makefile.in
|
||||
cd $(srcdir)/..; $(SHELL) config.status
|
21
Testing/NXStringTable.example
Normal file
21
Testing/NXStringTable.example
Normal file
|
@ -0,0 +1,21 @@
|
|||
/* This is an example of a string table file. Everything inside a comment
|
||||
is completely ignored, even if in "quotes", or \escape characters, etc.
|
||||
*/
|
||||
|
||||
"title" = "pattern II target 1";
|
||||
|
||||
/* This is an example of excape codes in the string table, codes */
|
||||
/* that are not one of abfnrtv are stripped of the \ character */
|
||||
"escapes" = "This is a tab \t and a return \n or a \a but not a \p";
|
||||
"escapes2" = "Well how about a \0? Guess not.";
|
||||
|
||||
/* more parameters, white space between tokens is ignored */
|
||||
"actualSize"
|
||||
=
|
||||
"0.000250 0.000250";
|
||||
|
||||
/* a key with no value assumes the value is the same as the key */
|
||||
"hoe322070.element";
|
||||
|
||||
/* this will produce an error */
|
||||
"unterminated"= "this is a string with no ending quote;
|
125
Testing/client.m
Normal file
125
Testing/client.m
Normal file
|
@ -0,0 +1,125 @@
|
|||
#include <stdio.h>
|
||||
#include <objects/SocketPort.h>
|
||||
#include <objects/Connection.h>
|
||||
#include <objects/Proxy.h>
|
||||
#include <objects/BinaryCoder.h>
|
||||
#include <assert.h>
|
||||
#include "server.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
id p;
|
||||
id callback_receiver = [Object new];
|
||||
id o;
|
||||
id localObj;
|
||||
unsigned long i = 4;
|
||||
id c;
|
||||
int j,k;
|
||||
foo f = {99, "cow", 9876543};
|
||||
/* foo f2; */
|
||||
foo *fp;
|
||||
const char *n;
|
||||
// int a3[3] = {66,77,88};
|
||||
struct myarray ma = {{55,66,77}};
|
||||
double dbl = 3.14159265358979323846264338327;
|
||||
double *dbl_ptr;
|
||||
char *string = "Hello from the client";
|
||||
small_struct small = {12};
|
||||
BOOL b;
|
||||
const char *type;
|
||||
|
||||
[Coder setDebugging:YES];
|
||||
[BinaryCoder setDebugging:YES];
|
||||
|
||||
#if NeXT_runtime
|
||||
[Proxy setProtocolForProxies:@protocol(AllProxies)];
|
||||
#endif
|
||||
|
||||
if (argc > 1)
|
||||
p = [Connection rootProxyAtName:"test2server" onHost:argv[1]];
|
||||
else
|
||||
p = [Connection rootProxyAtName:"test2server" onHost:""];
|
||||
c = [p connectionForProxy];
|
||||
|
||||
type = [c _typeForSelector:sel_get_any_uid("name")
|
||||
remoteTarget:[p targetForProxy]];
|
||||
printf(">>type = %s\n", type);
|
||||
|
||||
printf(">>list proxy's hash is 0x%x\n",
|
||||
(unsigned)[p hash]);
|
||||
printf(">>list proxy's self is 0x%x = 0x%x\n",
|
||||
(unsigned)[p self], (unsigned)p);
|
||||
n = [p name];
|
||||
printf(">>proxy's name is (%s)\n", n);
|
||||
[p print:">>This is a message from the client."];
|
||||
printf(">>getLong:(out) to server i = %lu\n", i);
|
||||
[p getLong:&i];
|
||||
printf(">>getLong:(out) from server i = %lu\n", i);
|
||||
assert(i == 3);
|
||||
o = [p objectAt:0];
|
||||
printf(">>object proxy's hash is 0x%x\n", (unsigned)[o hash]);
|
||||
[p shout];
|
||||
[p callbackNameOn:callback_receiver];
|
||||
/* this next line doesn't actually test callbacks, it tests
|
||||
sending the same object twice in the same message. */
|
||||
[p callbackNameOn:p];
|
||||
b = [p doBoolean:YES];
|
||||
printf(">>BOOL value is '%c' (0x%x)\n", b, (int)b);
|
||||
#if 0
|
||||
/* Both these cause problems because GCC encodes them as "*",
|
||||
indistinguishable from strings. */
|
||||
b = NO;
|
||||
[p getBoolean:&b];
|
||||
printf(">>BOOL reference is '%c' (0x%x)\n", b, (int)b);
|
||||
b = NO;
|
||||
[p getUCharPtr:&b];
|
||||
printf(">>UCHAR reference is '%c' (0x%x)\n", b, (int)b);
|
||||
#endif
|
||||
fp = [p sendStructPtr:&f];
|
||||
fp->i = 11;
|
||||
[p sendStruct:f];
|
||||
[p sendSmallStruct:small];
|
||||
[p sendStructArray:ma];
|
||||
#if 0
|
||||
/* returning structures isn't working yet. */
|
||||
f2 = [p returnStruct];
|
||||
printf(">>returned foo: i=%d s=%s l=%lu\n",
|
||||
f2.i, f2.s, f2.l);
|
||||
#endif
|
||||
[p sendDouble:dbl andFloat:98.6];
|
||||
dbl_ptr = [p doDoublePointer:&dbl];
|
||||
printf(">>got double %f from server\n", *dbl_ptr);
|
||||
[p sendCharPtrPtr:&string];
|
||||
/* testing "-perform:" */
|
||||
if (p != [p perform:sel_get_any_uid("self")])
|
||||
[Object error:"trying perform:"];
|
||||
/* testing "bycopy" */
|
||||
/* reverse the order on these next two and it doesn't crash,
|
||||
however, having manyArgs called always seems to crash.
|
||||
Was this problem here before object forward references?
|
||||
Check a snapshot.
|
||||
Hmm. It seems like a runtime selector-handling bug. */
|
||||
if (![p isProxy])
|
||||
[p manyArgs:1 :2 :3 :4 :5 :6 :7 :8 :9 :10 :11 :12];
|
||||
[p sendBycopy:callback_receiver];
|
||||
printf(">>returned float %f\n", [p returnFloat]);
|
||||
printf(">>returned double %f\n", [p returnDouble]);
|
||||
|
||||
localObj = [[Object alloc] init];
|
||||
[p addObject:localObj];
|
||||
k = [p count];
|
||||
for (j = 0; j < k; j++)
|
||||
{
|
||||
id remote_peer_obj = [p objectAt:j];
|
||||
printf("triangle %d object proxy's hash is 0x%x\n",
|
||||
j, (unsigned)[remote_peer_obj hash]);
|
||||
[remote_peer_obj release];
|
||||
remote_peer_obj = [p objectAt:j];
|
||||
printf("repeated triangle %d object proxy's hash is 0x%x\n",
|
||||
j, (unsigned)[remote_peer_obj hash]);
|
||||
}
|
||||
[c runConnectionWithTimeout:1500];
|
||||
[c dealloc];
|
||||
|
||||
exit(0);
|
||||
}
|
16
Testing/pipes.m
Normal file
16
Testing/pipes.m
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include <objects/StdioStream.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char b[100];
|
||||
int len;
|
||||
id s = [[StdioStream alloc] initWithPipeFrom:"cat /etc/group | sort"];
|
||||
|
||||
while ((len = [s readBytes:b length:99]) > 0)
|
||||
{
|
||||
b[len] = '\0';
|
||||
printf("[%d]: %s\n", len, b);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
66
Testing/server.h
Normal file
66
Testing/server.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
#ifndef _server_h
|
||||
#define _server_h
|
||||
|
||||
#include <objects/stdobjects.h>
|
||||
#include <objects/InvalidationListening.h>
|
||||
#include <objc/List.h>
|
||||
#include <objects/Connection.h>
|
||||
#include <objects/Array.h>
|
||||
|
||||
typedef struct _small_struct {
|
||||
unsigned char z;
|
||||
} small_struct;
|
||||
|
||||
typedef struct _foo {
|
||||
int i;
|
||||
char *s;
|
||||
unsigned long l;
|
||||
} foo;
|
||||
|
||||
struct myarray {
|
||||
int a[3];
|
||||
};
|
||||
|
||||
@protocol ServerProtocol
|
||||
- addObject: o;
|
||||
- objectAt: (unsigned)i;
|
||||
- (unsigned) count;
|
||||
- print: (const char *)msg;
|
||||
- getLong: (out unsigned long*)i;
|
||||
- (oneway void) shout;
|
||||
- callbackNameOn: obj;
|
||||
- bounce: sender count: (int)c;
|
||||
- (BOOL) doBoolean: (BOOL)b;
|
||||
- getBoolean: (BOOL*)bp;
|
||||
- getUCharPtr: (unsigned char *)ucp;
|
||||
- (foo*) sendStructPtr: (foo*)f;
|
||||
- sendStruct: (foo)f;
|
||||
- sendSmallStruct: (small_struct)small;
|
||||
- (foo) returnStruct;
|
||||
- sendArray: (int[3])a;
|
||||
- sendStructArray: (struct myarray)ma;
|
||||
- sendDouble: (double)d andFloat: (float)f;
|
||||
- (double*) doDoublePointer: (double*)d;
|
||||
- sendCharPtrPtr: (char**)sp;
|
||||
- sendBycopy: (bycopy id)o;
|
||||
- manyArgs: (int)i1 : (int)i2 : (int)i3 : (int)i4 : (int)i5 : (int)i6
|
||||
: (int)i7 : (int)i8 : (int)i9 : (int)i10 : (int)i11 : (int)i12;
|
||||
- (float) returnFloat;
|
||||
- (double) returnDouble;
|
||||
@end
|
||||
|
||||
#if NeXT_runtime
|
||||
@protocol AllProxies <ServerProtocol>
|
||||
- (const char *)name;
|
||||
- (unsigned) hash;
|
||||
- self;
|
||||
@end
|
||||
#endif
|
||||
|
||||
@interface Server : Object <ServerProtocol,InvalidationListening>
|
||||
{
|
||||
id theList;
|
||||
}
|
||||
@end
|
||||
|
||||
#endif /* _server_h */
|
221
Testing/server.m
Normal file
221
Testing/server.m
Normal file
|
@ -0,0 +1,221 @@
|
|||
#include <objects/stdobjects.h>
|
||||
#include <stdio.h>
|
||||
#include <objects/SocketPort.h>
|
||||
#include <objects/Connection.h>
|
||||
#include <objc/List.h>
|
||||
#include <objects/BinaryCoder.h>
|
||||
#include "server.h"
|
||||
|
||||
@implementation Server
|
||||
- init
|
||||
{
|
||||
theList = [[List alloc] init];
|
||||
return self;
|
||||
}
|
||||
- (unsigned) count
|
||||
{
|
||||
return [theList count];
|
||||
}
|
||||
- addObject: o
|
||||
{
|
||||
[theList addObject:o];
|
||||
return self;
|
||||
}
|
||||
- objectAt: (unsigned)i
|
||||
{
|
||||
return [theList objectAt:i];
|
||||
}
|
||||
- print: (const char *)msg
|
||||
{
|
||||
printf(">>%s\n", msg);
|
||||
return self;
|
||||
}
|
||||
- getLong: (out unsigned long*)i
|
||||
{
|
||||
printf(">>getLong:(out) from client %lu\n", *i);
|
||||
*i = 3;
|
||||
printf(">>getLong:(out) to client %lu\n", *i);
|
||||
return self;
|
||||
}
|
||||
- (oneway void) shout
|
||||
{
|
||||
printf(">>Ahhhhh\n");
|
||||
return;
|
||||
}
|
||||
- callbackNameOn: obj
|
||||
{
|
||||
printf(">>callback name is (%s)\n", [obj name]);
|
||||
return self;
|
||||
}
|
||||
/* sender must also respond to 'bounce:count:' */
|
||||
- bounce: sender count: (int)c
|
||||
{
|
||||
if (--c)
|
||||
[sender bounce:self count:c];
|
||||
return self;
|
||||
}
|
||||
- (BOOL) doBoolean: (BOOL)b
|
||||
{
|
||||
printf(">> got boolean '%c' (0x%x) from client\n", b, (unsigned int)b);
|
||||
return YES;
|
||||
}
|
||||
/* This causes problems, because the runtime encodes this as "*",
|
||||
a string! */
|
||||
- getBoolean: (BOOL*)bp
|
||||
{
|
||||
printf(">> got boolean pointer '%c' (0x%x) from client\n",
|
||||
*bp, (unsigned int)*bp);
|
||||
return self;
|
||||
}
|
||||
/* This also causes problems, because the runtime also encodes this as "*",
|
||||
a string! */
|
||||
- getUCharPtr: (unsigned char *)ucp
|
||||
{
|
||||
printf(">> got unsignec char pointer '%c' (0x%x) from client\n",
|
||||
*ucp, (unsigned int)*ucp);
|
||||
return self;
|
||||
}
|
||||
|
||||
/* This isn't working yet */
|
||||
- (foo*) sendStructPtr: (foo*)f
|
||||
{
|
||||
printf(">>reference: i=%d s=%s l=%lu\n",
|
||||
f->i, f->s, f->l);
|
||||
f->i = 88;
|
||||
return f;
|
||||
}
|
||||
- sendStruct: (foo)f
|
||||
{
|
||||
printf(">>value: i=%d s=%s l=%lu\n",
|
||||
f.i, f.s, f.l);
|
||||
f.i = 88;
|
||||
return self;
|
||||
}
|
||||
- sendSmallStruct: (small_struct)small
|
||||
{
|
||||
printf(">>small value struct: z=%d\n", small.z);
|
||||
return self;
|
||||
}
|
||||
/* Doesn't work. GCC __builtin_return doesn't let you return structs? */
|
||||
- (foo) returnStruct
|
||||
{
|
||||
foo f = {1, "horse", 987654};
|
||||
return f;
|
||||
}
|
||||
/* Doesn't work because GCC generates the wrong encoding: "@0@+8:+12^i+16" */
|
||||
- sendArray: (int[3])a
|
||||
{
|
||||
printf(">> array %d %d %d\n", a[0], a[1], a[2]);
|
||||
return self;
|
||||
}
|
||||
- sendStructArray: (struct myarray)ma
|
||||
{
|
||||
printf(">>struct array %d %d %d\n", ma.a[0], ma.a[1], ma.a[2]);
|
||||
return self;
|
||||
}
|
||||
|
||||
- sendDouble: (double)d andFloat: (float)f
|
||||
{
|
||||
printf(">> double %f, float %f\n", d, f);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (double*) doDoublePointer: (double*)d
|
||||
{
|
||||
printf(">> got double %f from client\n", *d);
|
||||
*d = 1.234567;
|
||||
printf(">> returning double %f to client\n", *d);
|
||||
return d;
|
||||
}
|
||||
|
||||
- sendCharPtrPtr: (char**)sp
|
||||
{
|
||||
printf(">> got char**, string %s\n", *sp);
|
||||
return self;
|
||||
}
|
||||
|
||||
- sendBycopy: (bycopy id)o
|
||||
{
|
||||
printf(">> bycopy class is %s\n", [o name]);
|
||||
[o free];
|
||||
return self;
|
||||
}
|
||||
- manyArgs: (int)i1 : (int)i2 : (int)i3 : (int)i4 : (int)i5 : (int)i6
|
||||
: (int)i7 : (int)i8 : (int)i9 : (int)i10 : (int)i11 : (int)i12
|
||||
{
|
||||
printf(">> manyArgs: %d %d %d %d %d %d %d %d %d %d %d %d\n",
|
||||
i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (float) returnFloat
|
||||
{
|
||||
static float f = 2.3456789;
|
||||
return f;
|
||||
}
|
||||
|
||||
- (double) returnDouble
|
||||
{
|
||||
/* static <This is crashing gcc ss-940902 config'ed for irix5.1,
|
||||
but running on irix5.2> */
|
||||
double d = 4.567891234;
|
||||
return d;
|
||||
}
|
||||
|
||||
- senderIsInvalid: anObj
|
||||
{
|
||||
if ([anObj isKindOf:[Connection class]])
|
||||
{
|
||||
id objList;
|
||||
int i, j, count, listCount = [theList count];
|
||||
objList = [anObj proxies];
|
||||
count = [objList count];
|
||||
/* This contortion avoids List's calling -isEqual: on the proxy */
|
||||
for (i = 0; i < count; i++)
|
||||
for (j = 0; j < [theList count]; j++)
|
||||
if ([theList objectAt:j] == [objList objectAtIndex:i])
|
||||
[theList removeObjectAt:j];
|
||||
[objList free];
|
||||
if (listCount != [theList count])
|
||||
printf("$$$$$ senderIsInvalid: removed from theList\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
[self error:"non Connection is invalid"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (Connection*) connection: ancestor didConnect: newConn
|
||||
{
|
||||
printf("%s\n", sel_get_name(_cmd));
|
||||
[newConn registerForInvalidationNotification:self];
|
||||
[newConn setDelegate:self];
|
||||
return newConn;
|
||||
}
|
||||
@end
|
||||
|
||||
int main()
|
||||
{
|
||||
id l = [[Server alloc] init];
|
||||
id o = [[Object alloc] init];
|
||||
double d;
|
||||
Connection *c;
|
||||
|
||||
[BinaryCoder setDebugging:YES];
|
||||
|
||||
#if NeXT_runtime
|
||||
[Proxy setProtocolForProxies:@protocol(AllProxies)];
|
||||
#endif
|
||||
c = [Connection newRegisteringAtName:"test2server" withRootObject:l];
|
||||
[c registerForInvalidationNotification:l];
|
||||
[c setDelegate:l];
|
||||
|
||||
[l addObject:o];
|
||||
d = [l returnDouble];
|
||||
printf("got double %f\n", d);
|
||||
printf("list's hash is 0x%x\n", (unsigned)[l hash]);
|
||||
printf("object's hash is 0x%x\n", (unsigned)[o hash]);
|
||||
[c runConnection];
|
||||
|
||||
exit(0);
|
||||
}
|
113
Testing/test01.m
Normal file
113
Testing/test01.m
Normal file
|
@ -0,0 +1,113 @@
|
|||
|
||||
#include <objc/Object.h>
|
||||
#include <objects/objects.h>
|
||||
|
||||
#if (__svr4__)
|
||||
long lrand48();
|
||||
#define random lrand48
|
||||
#else
|
||||
long random();
|
||||
#endif
|
||||
|
||||
@interface Collection (TestingExtras)
|
||||
- printCount;
|
||||
@end
|
||||
@implementation Collection (TestingExtras)
|
||||
- printCount
|
||||
{
|
||||
printf("%s: count=%d\n", [self name], [self count]);
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
void checkSameContents(id objectslist)
|
||||
{
|
||||
unsigned i, c = [objectslist count];
|
||||
|
||||
for (i = 1; i < c; i++)
|
||||
if (![[objectslist objectAtIndex:0]
|
||||
contentsEqual:[objectslist objectAtIndex:i]])
|
||||
printf("collection 0 does not have same contents as collection %d\n", i);
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
int i, e;
|
||||
|
||||
id array = [[Array alloc] initWithType:@encode(int)];
|
||||
id bag = [[Bag alloc] initWithType:"i"];
|
||||
id stack = [[Stack alloc] initWithType:"i"];
|
||||
id queue = [[Queue alloc] initWithType:"i"];
|
||||
id gaparray = [[GapArray alloc] initWithType:"i"];
|
||||
id llist = [[EltNodeCollector alloc] initWithType:"i"
|
||||
nodeCollector:[[LinkedList alloc] init]
|
||||
nodeClass:[LinkedListEltNode class]];
|
||||
id bt = [[EltNodeCollector alloc] initWithType:"i"
|
||||
nodeCollector:[[BinaryTree alloc] init]
|
||||
nodeClass:[BinaryTreeEltNode class]];
|
||||
id rt = [[EltNodeCollector alloc] initWithType:"i"
|
||||
nodeCollector:[[RBTree alloc] init]
|
||||
nodeClass:[RBTreeEltNode class]];
|
||||
id st = [[EltNodeCollector alloc] initWithType:"i"
|
||||
nodeCollector:[[SplayTree alloc] init]
|
||||
nodeClass:[BinaryTreeEltNode class]];
|
||||
id foo = [[Array alloc] initWithType:"i"];
|
||||
|
||||
id collections = [DelegatePool new];
|
||||
|
||||
[collections delegatePoolAddObject:array];
|
||||
[collections delegatePoolAddObject:llist];
|
||||
[collections delegatePoolAddObject:bag];
|
||||
[collections delegatePoolAddObject:stack];
|
||||
[collections delegatePoolAddObject:queue];
|
||||
[collections delegatePoolAddObject:gaparray];
|
||||
[collections delegatePoolAddObject:bt];
|
||||
[collections delegatePoolAddObject:rt];
|
||||
[collections delegatePoolAddObject:st];
|
||||
[collections delegatePoolAddObject:foo];
|
||||
|
||||
printf("delegatePool filled, count=%d\n",
|
||||
[[collections delegatePoolCollection] count]);
|
||||
|
||||
[collections addElement:99];
|
||||
[collections printCount];
|
||||
|
||||
printf("Adding numbers...\n");
|
||||
for (i = 0; i < 17; i++)
|
||||
{
|
||||
e = random() % 99;
|
||||
printf("%2d ", e);
|
||||
[collections addElement:e];
|
||||
}
|
||||
printf("\ncollections filled\n\n");
|
||||
[collections printForDebugger];
|
||||
|
||||
{
|
||||
BOOL testzero (elt e)
|
||||
{
|
||||
if (e.void_ptr_u == 0) return NO;
|
||||
else return YES;
|
||||
}
|
||||
if ([array trueForAllElementsByCalling:testzero])
|
||||
printf("Array contains no zero's\n");
|
||||
}
|
||||
|
||||
checkSameContents([collections delegatePoolCollection]);
|
||||
|
||||
printf("\nremoving 99\n\n");
|
||||
[collections removeElement:99];
|
||||
|
||||
[foo removeElement:[foo minElement]];
|
||||
[foo addElement:99];
|
||||
printf("Collections 0 and 9 should mismatch\n");
|
||||
[collections printForDebugger];
|
||||
|
||||
checkSameContents([collections delegatePoolCollection]);
|
||||
|
||||
[collections empty];
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
41
Testing/test02.m
Normal file
41
Testing/test02.m
Normal file
|
@ -0,0 +1,41 @@
|
|||
|
||||
#include <objects/objects.h>
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
id dict = [[Dictionary alloc] initWithType:"*"
|
||||
keyType:"*"];
|
||||
id translator = [[Dictionary alloc] initWithType:"*"
|
||||
keyType:"*"];
|
||||
id mc;
|
||||
|
||||
[dict putElement:"herd" atKey:"cow"];
|
||||
[dict putElement:"pack" atKey:"dog"];
|
||||
[dict putElement:"school" atKey:"fish"];
|
||||
[dict putElement:"flock" atKey:"bird"];
|
||||
[dict putElement:"pride" atKey:"cat"];
|
||||
[dict putElement:"gaggle" atKey:"goose"];
|
||||
[dict printForDebugger];
|
||||
printf("removing goose\n");
|
||||
[dict removeElementAtKey:"goose"];
|
||||
[dict printForDebugger];
|
||||
|
||||
[translator putElement:"cow" atKey:"vache"];
|
||||
[translator putElement:"dog" atKey:"chien"];
|
||||
[translator putElement:"fish" atKey:"poisson"];
|
||||
[translator putElement:"bird" atKey:"oisseau"];
|
||||
[translator putElement:"cat" atKey:"chat"];
|
||||
|
||||
mc = [[MappedCollector alloc] initCollection:dict map:translator];
|
||||
[mc printForDebugger];
|
||||
|
||||
[mc free];
|
||||
[dict free];
|
||||
[translator free];
|
||||
|
||||
exit(0);
|
||||
|
||||
}
|
||||
|
||||
|
44
Testing/test03.m
Normal file
44
Testing/test03.m
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
#include <objects/objects.h>
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
id array = [[Array alloc] initWithType:@encode(int)];
|
||||
id bag;
|
||||
id llist;
|
||||
id btree;
|
||||
|
||||
[array addElementsCount:6, ((elt)0),((elt)1),((elt)5),((elt)3),
|
||||
((elt)4),((elt)2)];
|
||||
bag = [array shallowCopyAs:[Bag class]];
|
||||
llist = [[EltNodeCollector alloc] initWithType:@encode(int)
|
||||
nodeCollector:[[LinkedList alloc] init]
|
||||
nodeClass:[LinkedListEltNode class]];
|
||||
[llist addContentsOf:array];
|
||||
|
||||
btree = [[EltNodeCollector alloc] initWithType:@encode(int)
|
||||
nodeCollector:[[BinaryTree alloc] init]
|
||||
nodeClass:[BinaryTreeEltNode class]];
|
||||
[btree addContentsOf:array];
|
||||
printf("btree count = %d\n", [btree count]);
|
||||
|
||||
/* tmp test */
|
||||
/*
|
||||
if (typeof((id)0) != typeof(id))
|
||||
printf("typeof error\n");
|
||||
*/
|
||||
|
||||
[array printForDebugger];
|
||||
[bag printForDebugger];
|
||||
[llist printForDebugger];
|
||||
[btree printForDebugger];
|
||||
|
||||
/* foo = [array shallowCopyAs:[Object class]];
|
||||
Shouldn't the compiler complain about this?
|
||||
Object does not conform to <Collecting> */
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
44
Testing/test04.m
Normal file
44
Testing/test04.m
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
#include <objects/objects.h>
|
||||
|
||||
#define N 20
|
||||
|
||||
#if (sun && __svr4__)
|
||||
long lrand48();
|
||||
#define random lrand48
|
||||
#else
|
||||
long random();
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
int i;
|
||||
short s, s1, s2;
|
||||
|
||||
Heap* heap = [[Heap alloc] initWithType:"s"];
|
||||
Array* array = [[Array alloc] initWithType:"s"];
|
||||
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
s = (short)random();
|
||||
[heap addElement:s];
|
||||
[array addElement:s];
|
||||
}
|
||||
[array sortContents];
|
||||
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
s1 = [heap removeFirstElement].short_int_u;
|
||||
s2 = [array removeLastElement].short_int_u;
|
||||
printf("(%d,%d) ", s1, s2);
|
||||
if (s1 != s2)
|
||||
exit(1);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
/* cause an error */
|
||||
/* s = [heap elementAtIndex:999].short_int_u; */
|
||||
|
||||
[heap free];
|
||||
exit(0);
|
||||
}
|
17
Testing/test05.m
Normal file
17
Testing/test05.m
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <objects/objects.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
id a = [[Array alloc] initWithType:@encode(int)];
|
||||
int i;
|
||||
unsigned ret42 (arglist_t f) { return 42; }
|
||||
|
||||
[a addElementsCount:5,
|
||||
((elt)0),((elt)1),((elt)2),((elt)3),((elt)4)];
|
||||
[a printForDebugger];
|
||||
i = [a indexOfElement:99
|
||||
ifAbsentCall:ret42];
|
||||
|
||||
printf("This should be 42---> %d\n", i);
|
||||
exit(0);
|
||||
}
|
23
Testing/test06.m
Normal file
23
Testing/test06.m
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
#include <objects/objects.h>
|
||||
|
||||
#define N 20
|
||||
|
||||
int main()
|
||||
{
|
||||
int i;
|
||||
|
||||
Array* array = [[Array alloc] init];
|
||||
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
[array addObject:[[Object alloc] init]];
|
||||
}
|
||||
|
||||
[array makeObjectsPerform:@selector(name)];
|
||||
|
||||
[[array objectAtIndex:0] hash];
|
||||
[[array freeObjects] free];
|
||||
printf("no errors\n");
|
||||
exit(0);
|
||||
}
|
16
Testing/test07.m
Normal file
16
Testing/test07.m
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include <objects/objects.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
id a = [[Array alloc] initWithType:@encode(int)];
|
||||
int i;
|
||||
void plus2 (elt e) {printf("%d ", e.int_u+2);}
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
[a addElement:i];
|
||||
|
||||
[a withElementsCall:plus2];
|
||||
|
||||
printf("\n");
|
||||
exit(0);
|
||||
}
|
60
Testing/test08.m
Normal file
60
Testing/test08.m
Normal file
|
@ -0,0 +1,60 @@
|
|||
|
||||
#include <objects/objects.h>
|
||||
|
||||
void test(id objects)
|
||||
{
|
||||
TypedStream *stream;
|
||||
|
||||
[objects addElementsCount:6, ((elt)0),((elt)1),((elt)5),((elt)3),
|
||||
((elt)4),((elt)2)];
|
||||
printf("written ");
|
||||
[objects printForDebugger];
|
||||
|
||||
stream = objc_open_typed_stream_for_file("test08.data", OBJC_WRITEONLY);
|
||||
objc_write_root_object(stream, objects);
|
||||
objc_close_typed_stream(stream);
|
||||
[objects free];
|
||||
|
||||
stream = objc_open_typed_stream_for_file("test08.data", OBJC_READONLY);
|
||||
objc_read_object(stream, &objects);
|
||||
printf("read ");
|
||||
[objects printForDebugger];
|
||||
[objects free];
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
id objects;
|
||||
|
||||
objects = [[Array alloc] initWithType:@encode(int)];
|
||||
test(objects);
|
||||
|
||||
objects = [[Bag alloc] initWithType:@encode(int)];
|
||||
test(objects);
|
||||
|
||||
objects = [[Set alloc] initWithType:@encode(int)];
|
||||
test(objects);
|
||||
|
||||
objects = [[GapArray alloc] initWithType:@encode(int)];
|
||||
test(objects);
|
||||
|
||||
objects = [[EltNodeCollector alloc] initWithType:@encode(int)
|
||||
nodeCollector:[[LinkedList alloc] init]
|
||||
nodeClass:[LinkedListEltNode class]];
|
||||
test(objects);
|
||||
|
||||
objects = [[EltNodeCollector alloc] initWithType:@encode(int)
|
||||
nodeCollector:[[BinaryTree alloc] init]
|
||||
nodeClass:[BinaryTreeEltNode class]];
|
||||
test(objects);
|
||||
|
||||
/*
|
||||
objects = [[EltNodeCollector alloc] initWithType:@encode(int)
|
||||
nodeClass:[RBTreeEltNode class]];
|
||||
test(objects);
|
||||
*/
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
81
Testing/test09.m
Normal file
81
Testing/test09.m
Normal file
|
@ -0,0 +1,81 @@
|
|||
#include <objects/objects.h>
|
||||
|
||||
#if (sun && __svr4__)
|
||||
long lrand48();
|
||||
#define random lrand48
|
||||
#else
|
||||
long random();
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
int i;
|
||||
id node;
|
||||
id s = [[EltNodeCollector alloc] initWithType:@encode(int)
|
||||
nodeCollector:[[SplayTree alloc] init]
|
||||
nodeClass:[BinaryTreeEltNode class]];
|
||||
void foo (id o) {[o self];}
|
||||
|
||||
for (i = 1; i < 20; i++)
|
||||
[s addElement:(int)random()%99];
|
||||
[[s contentsCollector] binaryTreePrintForDebugger];
|
||||
[s free];
|
||||
|
||||
s = [[EltNodeCollector alloc] initWithType:@encode(int)
|
||||
nodeCollector:[[SplayTree alloc] init]
|
||||
nodeClass:[BinaryTreeEltNode class]];
|
||||
for (i = 1; i < 20; i++)
|
||||
[s addElement:(int)random()%99];
|
||||
[[s contentsCollector] binaryTreePrintForDebugger];
|
||||
[s removeElement:[s elementAtIndex:10]];
|
||||
[[s contentsCollector] binaryTreePrintForDebugger];
|
||||
[[s contentsCollector] withObjectsCall:foo];
|
||||
[s free];
|
||||
|
||||
s = [[EltNodeCollector alloc] initWithType:@encode(int)
|
||||
nodeCollector:[[BinaryTree alloc] init]
|
||||
nodeClass:[BinaryTreeEltNode class]];
|
||||
[s appendElement:'i'];
|
||||
[s insertElement:'h' before:'i'];
|
||||
[s insertElement:'g' before:'h'];
|
||||
[s insertElement:'f' before:'g'];
|
||||
[s insertElement:'e' after:'f'];
|
||||
[s insertElement:'d' before:'e'];
|
||||
[s insertElement:'c' after:'d'];
|
||||
[s insertElement:'b' after:'c'];
|
||||
[s insertElement:'a' after:'b'];
|
||||
[[s contentsCollector] binaryTreePrintForDebugger];
|
||||
[[s contentsCollector] binaryTreePrintForDebugger];
|
||||
|
||||
s = [[EltNodeCollector alloc] initWithType:@encode(char)
|
||||
nodeCollector:[[SplayTree alloc] init]
|
||||
nodeClass:[BinaryTreeEltNode class]];
|
||||
[s appendElement:(char)'i'];
|
||||
[s insertElement:(char)'h' before:(char)'i'];
|
||||
[s insertElement:(char)'g' before:(char)'h'];
|
||||
[s insertElement:(char)'f' before:(char)'g'];
|
||||
[s insertElement:(char)'e' after:(char)'f'];
|
||||
[s insertElement:(char)'d' before:(char)'e'];
|
||||
[s insertElement:(char)'c' after:(char)'d'];
|
||||
[s insertElement:(char)'b' after:(char)'c'];
|
||||
[s insertElement:(char)'a' after:(char)'b'];
|
||||
[[s contentsCollector] binaryTreePrintForDebugger];
|
||||
|
||||
[[s contentsCollector] splayNode:[s eltNodeWithElement:(char)'a']];
|
||||
[[s contentsCollector] binaryTreePrintForDebugger];
|
||||
|
||||
/*
|
||||
while ([s eltNodeWithElement:(char)'a']
|
||||
!= [[s contentsCollector] rootNode])
|
||||
{
|
||||
[[s contentsCollector] _doSplayOperationOnNode:
|
||||
[s eltNodeWithElement:(char)'a']];
|
||||
printf("===============================\n");
|
||||
[[s contentsCollector] binaryTreePrintForDebugger];
|
||||
}
|
||||
*/
|
||||
|
||||
if ((node = [s eltNodeWithElement:(char)'z']) != nil)
|
||||
[s error:"eltNodeWithElement: loses."];
|
||||
exit(0);
|
||||
}
|
35
Testing/test10.m
Normal file
35
Testing/test10.m
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <objects/objects.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
#if ELT_INCLUDES_DOUBLE
|
||||
|
||||
id a = [[Array alloc] initWithType:@encode(double)];
|
||||
elt e;
|
||||
double dbl;
|
||||
|
||||
printf("testing elt doubles\n");
|
||||
|
||||
[a addElement:(double)3.14];
|
||||
[a addElement:(double)1.41];
|
||||
[a addElement:(double)4.15];
|
||||
[a addElement:(double)1.59];
|
||||
[a addElement:(double)5.92];
|
||||
[a addElement:(double)9.26];
|
||||
|
||||
e = [a elementAtIndex:1];
|
||||
dbl = [a elementAtIndex:2].double_u;
|
||||
printf("dbl = %f\n", dbl);
|
||||
|
||||
[a addElementIfAbsent:(double)9.26];
|
||||
assert([a count] == 6);
|
||||
|
||||
[a removeElement:(double)3.14];
|
||||
assert([a count] == 5);
|
||||
|
||||
#endif /* ELT_INCLUDES_DOUBLE */
|
||||
|
||||
printf("no errors\n");
|
||||
exit(0);
|
||||
}
|
29
Testing/test11.m
Normal file
29
Testing/test11.m
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include <objc/NXStringTable.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
id table;
|
||||
int i, times;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: table_test filename repeat\n");
|
||||
fprintf(stderr, " filename is a stringtable format file.\n");
|
||||
fprintf(stderr, " repeat is a number of times to loop\n");
|
||||
exit(1);
|
||||
}
|
||||
if (argc == 3)
|
||||
times = atoi(argv[2]);
|
||||
else
|
||||
times = 1;
|
||||
|
||||
table = [[NXStringTable alloc] init];
|
||||
|
||||
for (i=0; i < times; i++) {
|
||||
[table readFromFile:argv[1]];
|
||||
printf("-----------------------------------------\n");
|
||||
[table writeToStream:stdout];
|
||||
}
|
||||
return 0;
|
||||
}
|
35
Testing/test12.m
Normal file
35
Testing/test12.m
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
#include <objects/Random.h>
|
||||
#include <objects/RNGBerkeley.h>
|
||||
#include <objects/RNGAdditiveCongruential.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
id r;
|
||||
id rng;
|
||||
int i;
|
||||
|
||||
r = [[Random alloc] init];
|
||||
printf("float\n");
|
||||
for (i = 0; i < 20; i++)
|
||||
printf("%f\n", [r randomFloat]);
|
||||
printf("doubles\n");
|
||||
for (i = 0; i < 20; i++)
|
||||
printf("%f\n", [r randomDouble]);
|
||||
|
||||
rng = [[RNGBerkeley alloc] init];
|
||||
printf("%s chi^2 = %f\n",
|
||||
[rng name], [Random chiSquareOfRandomGenerator:rng]);
|
||||
[r free];
|
||||
|
||||
rng = [[RNGAdditiveCongruential alloc] init];
|
||||
/*
|
||||
for (i = 0; i < 50; i++)
|
||||
printf("%ld\n", [r nextRandom]);
|
||||
*/
|
||||
printf("%s chi^2 = %f\n",
|
||||
[rng name], [Random chiSquareOfRandomGenerator:rng]);
|
||||
[r free];
|
||||
|
||||
exit(0);
|
||||
}
|
44
Testing/test13.m
Normal file
44
Testing/test13.m
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
From: Matthias Klose <doko@cs.tu-berlin.de>
|
||||
Date: Mon, 1 Aug 1994 21:17:20 +0200
|
||||
To: mccallum@cs.rochester.edu
|
||||
Subject: bug in libcoll-940725
|
||||
Reply-to: doko@cs.tu-berlin.de
|
||||
|
||||
Hello, the following code core dumps on Solaris 2.3 (compiled with gcc
|
||||
2.5.8 -g -O and with -g) and on NeXTstep 3.2 (gcc 2.5.8).
|
||||
Any hints?
|
||||
*/
|
||||
|
||||
#include <objects/Queue.h>
|
||||
|
||||
int main ()
|
||||
{
|
||||
Array *a;
|
||||
CircularArray *c;
|
||||
Queue *q;
|
||||
|
||||
a = [Array new];
|
||||
|
||||
[a prependObject: [Object new]];
|
||||
[a prependObject: [Object new]];
|
||||
[a prependObject: [Object new]];
|
||||
printf("count: %d\n", [a count]);
|
||||
[a insertObject: [Object new] atIndex: 2]; // ok!
|
||||
printf("count: %d\n", [a count]);
|
||||
|
||||
c = [[CircularArray alloc] initWithType:@encode(int)];
|
||||
[c prependElement: 3];
|
||||
[c prependElement: 2];
|
||||
[c prependElement: 1];
|
||||
[c insertElement:0 atIndex:2]; // core dump!
|
||||
|
||||
q = [Queue new];
|
||||
[q enqueueObject: [Object new]];
|
||||
[q enqueueObject: [Object new]];
|
||||
[q enqueueObject: [Object new]];
|
||||
printf("count: %d\n", [q count]);
|
||||
[q insertObject: [Object new] atIndex: 2]; // core dump!
|
||||
printf("count: %d\n", [q count]);
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue