Remove wpiutil and update to the new build system (#210)

This commit is contained in:
Thad House
2017-08-03 14:14:40 -07:00
committed by Peter Johnson
parent f43675e2bd
commit 5df7463663
168 changed files with 670 additions and 16084 deletions

347
src/arm-linux-jni/LICENSE Normal file
View File

@@ -0,0 +1,347 @@
The GNU General Public License (GPL)
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies of this license
document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to share
and change it. By contrast, the GNU General Public License is intended to
guarantee your freedom to share and change free software--to make sure the
software is free for all its users. This General Public License applies to
most of the Free Software Foundation's software and to any other program whose
authors commit to using it. (Some other Free Software Foundation software is
covered by the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not price. Our
General Public Licenses are designed to make sure that you have the freedom to
distribute copies of free software (and charge for this service if you wish),
that you receive source code or can get it if you want it, that you can change
the software or use pieces of it in new free programs; and that you know you
can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny
you these rights or to ask you to surrender the rights. These restrictions
translate to certain responsibilities for you if you distribute copies of the
software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for
a fee, you must give the recipients all the rights that you have. You must
make sure that they, too, receive or can get the source code. And you must
show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2)
offer you this license which gives you legal permission to copy, distribute
and/or modify the software.
Also, for each author's protection and ours, we want to make certain that
everyone understands that there is no warranty for this free software. If the
software is modified by someone else and passed on, we want its recipients to
know that what they have is not the original, so that any problems introduced
by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We
wish to avoid the danger that redistributors of a free program will
individually obtain patent licenses, in effect making the program proprietary.
To prevent this, we have made it clear that any patent must be licensed for
everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification
follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains a notice
placed by the copyright holder saying it may be distributed under the terms of
this General Public License. The "Program", below, refers to any such program
or work, and a "work based on the Program" means either the Program or any
derivative work under copyright law: that is to say, a work containing the
Program or a portion of it, either verbatim or with modifications and/or
translated into another language. (Hereinafter, translation is included
without limitation in the term "modification".) Each licensee is addressed as
"you".
Activities other than copying, distribution and modification are not covered by
this License; they are outside its scope. The act of running the Program is
not restricted, and the output from the Program is covered only if its contents
constitute a work based on the Program (independent of having been made by
running the Program). Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's source code as
you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this License
and to the absence of any warranty; and give any other recipients of the
Program a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and you may
at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it, thus
forming a work based on the Program, and copy and distribute such modifications
or work under the terms of Section 1 above, provided that you also meet all of
these conditions:
a) You must cause the modified files to carry prominent notices stating
that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or
in part contains or is derived from the Program or any part thereof, to be
licensed as a whole at no charge to all third parties under the terms of
this License.
c) If the modified program normally reads commands interactively when run,
you must cause it, when started running for such interactive use in the
most ordinary way, to print or display an announcement including an
appropriate copyright notice and a notice that there is no warranty (or
else, saying that you provide a warranty) and that users may redistribute
the program under these conditions, and telling the user how to view a copy
of this License. (Exception: if the Program itself is interactive but does
not normally print such an announcement, your work based on the Program is
not required to print an announcement.)
These requirements apply to the modified work as a whole. If identifiable
sections of that work are not derived from the Program, and can be reasonably
considered independent and separate works in themselves, then this License, and
its terms, do not apply to those sections when you distribute them as separate
works. But when you distribute the same sections as part of a whole which is a
work based on the Program, the distribution of the whole must be on the terms
of this License, whose permissions for other licensees extend to the entire
whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your
rights to work written entirely by you; rather, the intent is to exercise the
right to control the distribution of derivative or collective works based on
the Program.
In addition, mere aggregation of another work not based on the Program with the
Program (or with a work based on the Program) on a volume of a storage or
distribution medium does not bring the other work under the scope of this
License.
3. You may copy and distribute the Program (or a work based on it, under
Section 2) in object code or executable form under the terms of Sections 1 and
2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source
code, which must be distributed under the terms of Sections 1 and 2 above
on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to
give any third party, for a charge no more than your cost of physically
performing source distribution, a complete machine-readable copy of the
corresponding source code, to be distributed under the terms of Sections 1
and 2 above on a medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to
distribute corresponding source code. (This alternative is allowed only
for noncommercial distribution and only if you received the program in
object code or executable form with such an offer, in accord with
Subsection b above.)
The source code for a work means the preferred form of the work for making
modifications to it. For an executable work, complete source code means all
the source code for all modules it contains, plus any associated interface
definition files, plus the scripts used to control compilation and installation
of the executable. However, as a special exception, the source code
distributed need not include anything that is normally distributed (in either
source or binary form) with the major components (compiler, kernel, and so on)
of the operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the source
code from the same place counts as distribution of the source code, even though
third parties are not compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program except as
expressly provided under this License. Any attempt otherwise to copy, modify,
sublicense or distribute the Program is void, and will automatically terminate
your rights under this License. However, parties who have received copies, or
rights, from you under this License will not have their licenses terminated so
long as such parties remain in full compliance.
5. You are not required to accept this License, since you have not signed it.
However, nothing else grants you permission to modify or distribute the Program
or its derivative works. These actions are prohibited by law if you do not
accept this License. Therefore, by modifying or distributing the Program (or
any work based on the Program), you indicate your acceptance of this License to
do so, and all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the Program),
the recipient automatically receives a license from the original licensor to
copy, distribute or modify the Program subject to these terms and conditions.
You may not impose any further restrictions on the recipients' exercise of the
rights granted herein. You are not responsible for enforcing compliance by
third parties to this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues), conditions
are imposed on you (whether by court order, agreement or otherwise) that
contradict the conditions of this License, they do not excuse you from the
conditions of this License. If you cannot distribute so as to satisfy
simultaneously your obligations under this License and any other pertinent
obligations, then as a consequence you may not distribute the Program at all.
For example, if a patent license would not permit royalty-free redistribution
of the Program by all those who receive copies directly or indirectly through
you, then the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply and
the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or
other property right claims or to contest validity of any such claims; this
section has the sole purpose of protecting the integrity of the free software
distribution system, which is implemented by public license practices. Many
people have made generous contributions to the wide range of software
distributed through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing to
distribute software through any other system and a licensee cannot impose that
choice.
This section is intended to make thoroughly clear what is believed to be a
consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain
countries either by patents or by copyrighted interfaces, the original
copyright holder who places the Program under this License may add an explicit
geographical distribution limitation excluding those countries, so that
distribution is permitted only in or among countries not thus excluded. In
such case, this License incorporates the limitation as if written in the body
of this License.
9. The Free Software Foundation may publish revised and/or new versions of the
General Public License from time to time. Such new versions will be similar in
spirit to the present version, but may differ in detail to address new problems
or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any later
version", you have the option of following the terms and conditions either of
that version or of any later version published by the Free Software Foundation.
If the Program does not specify a version number of this License, you may
choose any version ever published by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free programs
whose distribution conditions are different, write to the author to ask for
permission. For software which is copyrighted by the Free Software Foundation,
write to the Free Software Foundation; we sometimes make exceptions for this.
Our decision will be guided by the two goals of preserving the free status of
all derivatives of our free software and of promoting the sharing and reuse of
software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE
PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE
PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible
use to the public, the best way to achieve this is to make it free software
which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach
them to the start of each source file to most effectively convey the exclusion
of warranty; and each file should have at least the "copyright" line and a
pointer to where the full notice is found.
One line to give the program's name and a brief idea of what it does.
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program 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 General Public License for
more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc., 59
Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this when it
starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free
software, and you are welcome to redistribute it under certain conditions;
type 'show c' for details.
The hypothetical commands 'show w' and 'show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may be
called something other than 'show w' and 'show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your school,
if any, to sign a "copyright disclaimer" for the program, if necessary. Here
is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
'Gnomovision' (which makes passes at compilers) written by James Hacker.
signature of Ty Coon, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General Public
License instead of this License.
"CLASSPATH" EXCEPTION TO THE GPL
Certain source files distributed by Oracle America and/or its affiliates are
subject to the following clarification and special exception to the GPL, but
only where Oracle has expressly included in the particular source file's header
the words "Oracle designates this particular file as subject to the "Classpath"
exception as provided by Oracle in the LICENSE file that accompanied this code."
Linking this library statically or dynamically with other modules is making
a combined work based on this library. Thus, the terms and conditions of
the GNU General Public License cover the whole combination.
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules,
and to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent module,
the terms and conditions of the license of that module. An independent
module is a module which is not derived from or based on this library. If
you modify this library, you may extend this exception to your version of
the library, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.

1960
src/arm-linux-jni/jni.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef _JAVASOFT_JNI_MD_H_
#define _JAVASOFT_JNI_MD_H_
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility)
#define JNIEXPORT __attribute__((visibility("default")))
#define JNIIMPORT __attribute__((visibility("default")))
#else
#define JNIEXPORT
#define JNIIMPORT
#endif
#define JNICALL
typedef int jint;
#ifdef _LP64 /* 64-bit Solaris */
typedef long jlong;
#else
typedef long long jlong;
#endif
typedef signed char jbyte;
#endif /* !_JAVASOFT_JNI_MD_H_ */

View File

@@ -0,0 +1,9 @@
#include "ntcore.h"
#include "nt_Value.h"
#include <iostream>
int main() {
nt::SetEntryValue("MyValue", nt::Value::MakeString("Hello World"));
std::cout << nt::GetEntryValue("MyValue")->GetString() << std::endl;
}

View File

@@ -0,0 +1,17 @@
package edu.wpi.first.wpilibj.networktables;
public class ConnectionInfo {
public final String remote_id;
public final String remote_ip;
public final int remote_port;
public final long last_update;
public final int protocol_version;
public ConnectionInfo(String remote_id, String remote_ip, int remote_port, long last_update, int protocol_version) {
this.remote_id = remote_id;
this.remote_ip = remote_ip;
this.remote_port = remote_port;
this.last_update = last_update;
this.protocol_version = protocol_version;
}
}

View File

@@ -0,0 +1,15 @@
package edu.wpi.first.wpilibj.networktables;
public class EntryInfo {
public final String name;
public final int type;
public final int flags;
public final long last_change;
public EntryInfo(String name, int type, int flags, long last_change) {
this.name = name;
this.type = type;
this.flags = flags;
this.last_change = last_change;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
package edu.wpi.first.wpilibj.networktables;
import edu.wpi.first.wpilibj.tables.TableKeyNotDefinedException;
/**
* An exception throw when the lookup a a key-value fails in a {@link NetworkTable}
*
* @deprecated to provide backwards compatability for new api
*
* @author Mitchell
*
*/
@Deprecated
public class NetworkTableKeyNotDefined extends TableKeyNotDefinedException {
/**
* @param key the key that was not defined in the table
*/
public NetworkTableKeyNotDefined(String key) {
super(key);
}
}

View File

@@ -0,0 +1,171 @@
package edu.wpi.first.wpilibj.networktables;
import edu.wpi.first.wpilibj.tables.*;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import edu.wpi.first.wpiutil.RuntimeDetector;
public class NetworkTablesJNI {
static boolean libraryLoaded = false;
static File jniLibrary = null;
static {
if (!libraryLoaded) {
try {
System.loadLibrary("ntcore");
} catch (UnsatisfiedLinkError e) {
try {
String resname = RuntimeDetector.getLibraryResource("ntcore");
InputStream is = NetworkTablesJNI.class.getResourceAsStream(resname);
if (is != null) {
// create temporary file
if (System.getProperty("os.name").startsWith("Windows"))
jniLibrary = File.createTempFile("NetworkTablesJNI", ".dll");
else if (System.getProperty("os.name").startsWith("Mac"))
jniLibrary = File.createTempFile("libNetworkTablesJNI", ".dylib");
else
jniLibrary = File.createTempFile("libNetworkTablesJNI", ".so");
// flag for delete on exit
jniLibrary.deleteOnExit();
OutputStream os = new FileOutputStream(jniLibrary);
byte[] buffer = new byte[1024];
int readBytes;
try {
while ((readBytes = is.read(buffer)) != -1) {
os.write(buffer, 0, readBytes);
}
} finally {
os.close();
is.close();
}
System.load(jniLibrary.getAbsolutePath());
} else {
System.loadLibrary("ntcore");
}
} catch (IOException ex) {
ex.printStackTrace();
System.exit(1);
}
}
libraryLoaded = true;
}
}
public static final int NT_NET_MODE_NONE = 0x00;
public static final int NT_NET_MODE_SERVER = 0x01;
public static final int NT_NET_MODE_CLIENT = 0x02;
public static final int NT_NET_MODE_STARTING = 0x04;
public static final int NT_NET_MODE_FAILURE = 0x08;
public static native boolean containsKey(String key);
public static native int getType(String key);
public static native boolean putBoolean(String key, boolean value);
public static native boolean putDouble(String key, double value);
public static native boolean putString(String key, String value);
public static native boolean putRaw(String key, byte[] value);
public static native boolean putRaw(String key, ByteBuffer value, int len);
public static native boolean putBooleanArray(String key, boolean[] value);
public static native boolean putDoubleArray(String key, double[] value);
public static native boolean putStringArray(String key, String[] value);
public static native void forcePutBoolean(String key, boolean value);
public static native void forcePutDouble(String key, double value);
public static native void forcePutString(String key, String value);
public static native void forcePutRaw(String key, byte[] value);
public static native void forcePutRaw(String key, ByteBuffer value, int len);
public static native void forcePutBooleanArray(String key, boolean[] value);
public static native void forcePutDoubleArray(String key, double[] value);
public static native void forcePutStringArray(String key, String[] value);
public static native Object getValue(String key) throws TableKeyNotDefinedException;
public static native boolean getBoolean(String key) throws TableKeyNotDefinedException;
public static native double getDouble(String key) throws TableKeyNotDefinedException;
public static native String getString(String key) throws TableKeyNotDefinedException;
public static native byte[] getRaw(String key) throws TableKeyNotDefinedException;
public static native boolean[] getBooleanArray(String key) throws TableKeyNotDefinedException;
public static native double[] getDoubleArray(String key) throws TableKeyNotDefinedException;
public static native String[] getStringArray(String key) throws TableKeyNotDefinedException;
public static native Object getValue(String key, Object defaultValue);
public static native boolean getBoolean(String key, boolean defaultValue);
public static native double getDouble(String key, double defaultValue);
public static native String getString(String key, String defaultValue);
public static native byte[] getRaw(String key, byte[] defaultValue);
public static native boolean[] getBooleanArray(String key, boolean[] defaultValue);
public static native double[] getDoubleArray(String key, double[] defaultValue);
public static native String[] getStringArray(String key, String[] defaultValue);
public static native boolean setDefaultBoolean(String key, boolean defaultValue);
public static native boolean setDefaultDouble(String key, double defaultValue);
public static native boolean setDefaultString(String key, String defaultValue);
public static native boolean setDefaultRaw(String key, byte[] defaultValue);
public static native boolean setDefaultBooleanArray(String key, boolean[] defaultValue);
public static native boolean setDefaultDoubleArray(String key, double[] defaultValue);
public static native boolean setDefaultStringArray(String key, String[] defaultValue);
public static native void setEntryFlags(String key, int flags);
public static native int getEntryFlags(String key);
public static native void deleteEntry(String key);
public static native void deleteAllEntries();
public static native EntryInfo[] getEntries(String prefix, int types);
public static native void flush();
@FunctionalInterface
public interface EntryListenerFunction {
void apply(int uid, String key, Object value, int flags);
}
public static native int addEntryListener(String prefix, EntryListenerFunction listener, int flags);
public static native void removeEntryListener(int entryListenerUid);
@FunctionalInterface
public interface ConnectionListenerFunction {
void apply(int uid, boolean connected, ConnectionInfo conn);
}
public static native int addConnectionListener(ConnectionListenerFunction listener, boolean immediateNotify);
public static native void removeConnectionListener(int connListenerUid);
// public static native void createRpc(String key, byte[] def, IRpc rpc);
// public static native void createRpc(String key, ByteBuffer def, int def_len, IRpc rpc);
public static native byte[] getRpc(String key) throws TableKeyNotDefinedException;
public static native byte[] getRpc(String key, byte[] defaultValue);
public static native int callRpc(String key, byte[] params);
public static native int callRpc(String key, ByteBuffer params, int params_len);
// public static native byte[] getRpcResultBlocking(int callUid);
// public static native byte[] getRpcResultNonblocking(int callUid) throws RpcNoResponseException;
public static native void setNetworkIdentity(String name);
public static native int getNetworkMode();
public static native void startServer(String persistFilename, String listenAddress, int port);
public static native void stopServer();
public static native void startClient();
public static native void startClient(String serverName, int port);
public static native void startClient(String[] serverNames, int[] ports);
public static native void stopClient();
public static native void setServer(String serverName, int port);
public static native void setServer(String[] serverNames, int[] ports);
public static native void startDSClient(int port);
public static native void stopDSClient();
public static native void setUpdateRate(double interval);
public static native ConnectionInfo[] getConnections();
public static native void savePersistent(String filename) throws PersistentException;
public static native String[] loadPersistent(String filename) throws PersistentException; // returns warnings
public static native long now();
@FunctionalInterface
public interface LoggerFunction {
void apply(int level, String file, int line, String msg);
}
public static native void setLogger(LoggerFunction func, int minLevel);
}

View File

@@ -0,0 +1,18 @@
package edu.wpi.first.wpilibj.networktables;
import java.io.IOException;
/**
* An exception thrown when persistent load/save fails in a {@link NetworkTable}
*
*/
public class PersistentException extends IOException {
/**
* @param message The error message
*/
public PersistentException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,50 @@
package edu.wpi.first.wpilibj.networktables2.type;
/**
* @deprecated Use ArrayList instead.
*/
@Deprecated
public class ArrayData {
private Object[] data = new Object[0];
protected Object getAsObject(int index) {
return data[index];
}
protected void _set(int index, Object value) {
data[index] = value;
}
protected void _add(Object value) {
setSize(size() + 1);
data[size() - 1] = value;
}
public void remove(int index) {
if (index < 0 || index >= size())
throw new IndexOutOfBoundsException();
if (index < size() - 1)
System.arraycopy(data, index + 1, data, index, size() - index - 1);
setSize(size() - 1);
}
public void setSize(int size) {
if (size == data.length)
return;
Object[] newArray = new Object[size];
if (size < data.length)
System.arraycopy(data, 0, newArray, 0, size);
else {
System.arraycopy(data, 0, newArray, 0, data.length);
for (int i = data.length; i < newArray.length; ++i)
newArray[i] = null;
}
data = newArray;
}
public int size() {
return data.length;
}
public Object[] getDataArray() {
return data;
}
public void setDataArray(Object[] value) {
data = value;
}
}

View File

@@ -0,0 +1,17 @@
package edu.wpi.first.wpilibj.networktables2.type;
/**
* @deprecated Use {@literal ArrayList<Boolean>} instead.
*/
@Deprecated
public class BooleanArray extends ArrayData {
public boolean get(int index) {
return ((Boolean)getAsObject(index)).booleanValue();
}
public void set(int index, boolean value) {
_set(index, value?Boolean.TRUE:Boolean.FALSE);
}
public void add(boolean value) {
_add(value?Boolean.TRUE:Boolean.FALSE);
}
}

View File

@@ -0,0 +1,17 @@
package edu.wpi.first.wpilibj.networktables2.type;
/**
* @deprecated Use {@literal ArrayList<Double>} instead.
*/
@Deprecated
public class NumberArray extends ArrayData {
public double get(int index) {
return ((Double)getAsObject(index)).doubleValue();
}
public void set(int index, double value) {
_set(index, new Double(value));
}
public void add(double value) {
_add(new Double(value));
}
}

View File

@@ -0,0 +1,17 @@
package edu.wpi.first.wpilibj.networktables2.type;
/**
* @deprecated Use {@literal ArrayList<String>} instead.
*/
@Deprecated
public class StringArray extends ArrayData {
public String get(int index) {
return ((String)getAsObject(index));
}
public void set(int index, String value) {
_set(index, value);
}
public void add(String value) {
_add(value);
}
}

View File

@@ -0,0 +1,37 @@
package edu.wpi.first.wpilibj.tables;
/**
* Represents an object that has a remote connection
*
* @author Mitchell
*
*/
public interface IRemote {
/**
* Register an object to listen for connection and disconnection events
*
* @param listener the listener to be register
* @param immediateNotify if the listener object should be notified of the current connection state
*/
public void addConnectionListener(IRemoteConnectionListener listener, boolean immediateNotify);
/**
* Unregister a listener from connection events
*
* @param listener the listener to be unregistered
*/
public void removeConnectionListener(IRemoteConnectionListener listener);
/**
* Get the current state of the objects connection
* @return the current connection state
*/
public boolean isConnected();
/**
* If the object is acting as a server
* @return if the object is a server
*/
public boolean isServer();
}

View File

@@ -0,0 +1,40 @@
package edu.wpi.first.wpilibj.tables;
import edu.wpi.first.wpilibj.networktables.ConnectionInfo;
/**
* A listener that listens for connection changes in a {@link IRemote} object
*
* @author Mitchell
*
*/
public interface IRemoteConnectionListener {
/**
* Called when an IRemote is connected
* @param remote the object that connected
*/
public void connected(IRemote remote);
/**
* Called when an IRemote is disconnected
* @param remote the object that disconnected
*/
public void disconnected(IRemote remote);
/**
* Extended version of connected called when an IRemote is connected.
* Contains the connection info of the connected remote
* @param remote the object that connected
* @param info the connection info for the connected remote
*/
default public void connectedEx(IRemote remote, ConnectionInfo info) {
connected(remote);
}
/**
* Extended version of connected called when an IRemote is disconnected.
* Contains the connection info of the disconnected remote
* @param remote the object that disconnected
* @param info the connection info for the disconnected remote
*/
default public void disconnectedEx(IRemote remote, ConnectionInfo info) {
disconnected(remote);
}
}

View File

@@ -0,0 +1,634 @@
package edu.wpi.first.wpilibj.tables;
import java.nio.ByteBuffer;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* A table whose values can be read and written to
*/
public interface ITable {
/**
* Checks the table and tells if it contains the specified key
*
* @param key the key to search for
* @return true if the table as a value assigned to the given key
*/
public boolean containsKey(String key);
/**
* @param key the key to search for
* @return true if there is a subtable with the key which contains at least
* one key/subtable of its own
*/
public boolean containsSubTable(String key);
/**
* Returns the table at the specified key. If there is no table at the
* specified key, it will create a new table
*
* @param key the name of the table relative to this one
* @return a sub table relative to this one
*/
public ITable getSubTable(String key);
/**
* @param types bitmask of types; 0 is treated as a "don't care".
* @return keys currently in the table
*/
public Set<String> getKeys(int types);
/**
* @return keys currently in the table
*/
public Set<String> getKeys();
/**
* @return subtables currently in the table
*/
public Set<String> getSubTables();
/**
* Makes a key's value persistent through program restarts.
* The key cannot be null.
*
* @param key the key name
*/
public void setPersistent(String key);
/**
* Stop making a key's value persistent through program restarts.
* The key cannot be null.
*
* @param key the key name
*/
public void clearPersistent(String key);
/**
* Returns whether the value is persistent through program restarts.
* The key cannot be null.
*
* @param key the key name
* @return True if the value is persistent.
*/
public boolean isPersistent(String key);
/**
* Sets flags on the specified key in this table. The key can
* not be null.
*
* @param key the key name
* @param flags the flags to set (bitmask)
*/
public void setFlags(String key, int flags);
/**
* Clears flags on the specified key in this table. The key can
* not be null.
*
* @param key the key name
* @param flags the flags to clear (bitmask)
*/
public void clearFlags(String key, int flags);
/**
* Returns the flags for the specified key.
*
* @param key the key name
* @return the flags, or 0 if the key is not defined
*/
public int getFlags(String key);
/**
* Deletes the specified key in this table. The key can
* not be null.
*
* @param key the key name
*/
public void delete(String key);
/**
* Gets the value associated with a key as an object. If the key does not
* exist, it will return the default value
* NOTE: If the value is a double, it will return a Double object,
* not a primitive. To get the primitive, use
* {@link #getDouble(String, double)}.
* @param key the key of the value to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method {@link #getValue(String, Object)}.
*/
@Deprecated
public Object getValue(String key) throws TableKeyNotDefinedException;
/**
* Gets the value associated with a key as an object.
* NOTE: If the value is a double, it will return a Double object,
* not a primitive. To get the primitive, use
* {@link #getDouble(String, double)}.
* @param key the key of the value to look up
* @param defaultValue the default value if the key is null
* @return the value associated with the given key
*/
public Object getValue(String key, Object defaultValue);
/**
* Put a value in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
* @throws IllegalArgumentException when the value is not supported by the
* table
*/
public boolean putValue(String key, Object value)
throws IllegalArgumentException;
/**
* Retrieve an array data type from the table.
* @param key the key to be assigned to
* @param externalValue the array data type to retreive into
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated Use get*Array functions instead.
*/
@Deprecated
public void retrieveValue(String key, Object externalValue) throws TableKeyNotDefinedException;
/**
* Put a number in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
public boolean putNumber(String key, double value);
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doens't exist.
* @return False if the table key exists with a different type
*/
public boolean setDefaultNumber(String key, double defaultValue);
/**
* Returns the number the key maps to.
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method {@link #getNumber(String, double)}.
*/
@Deprecated
public double getNumber(String key) throws TableKeyNotDefinedException;
/**
* Returns the number the key maps to. If the key does not exist or is of
* different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
public double getNumber(String key, double defaultValue);
/**
* Put a string in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
public boolean putString(String key, String value);
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doens't exist.
* @return False if the table key exists with a different type
*/
public boolean setDefaultString(String key, String defaultValue);
/**
* Returns the string the key maps to.
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method {@link #getString(String, String)}.
*/
@Deprecated
public String getString(String key) throws TableKeyNotDefinedException;
/**
* Returns the string the key maps to. If the key does not exist or is of
* different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
public String getString(String key, String defaultValue);
/**
* Put a boolean in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
public boolean putBoolean(String key, boolean value);
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doens't exist.
* @return False if the table key exists with a different type
*/
public boolean setDefaultBoolean(String key, boolean defaultValue);
/**
* Returns the boolean the key maps to.
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method {@link #getBoolean(String, boolean)}.
*/
@Deprecated
public boolean getBoolean(String key) throws TableKeyNotDefinedException;
/**
* Returns the boolean the key maps to. If the key does not exist or is of
* different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
public boolean getBoolean(String key, boolean defaultValue);
/**
* Put a boolean array in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
public boolean putBooleanArray(String key, boolean[] value);
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doens't exist.
* @return False if the table key exists with a different type
*/
public boolean setDefaultBooleanArray(String key, boolean[] defaultValue);
/**
* Put a boolean array in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
public boolean putBooleanArray(String key, Boolean[] value);
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doens't exist.
* @return False if the table key exists with a different type
*/
public boolean setDefaultBooleanArray(String key, Boolean[] defaultValue);
/**
* Returns the boolean array the key maps to.
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method {@link #getBooleanArray(String, boolean[])}.
*/
@Deprecated
public boolean[] getBooleanArray(String key) throws TableKeyNotDefinedException;
/**
* Returns the boolean array the key maps to. If the key does not exist or is
* of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
public boolean[] getBooleanArray(String key, boolean[] defaultValue);
/**
* Returns the boolean array the key maps to. If the key does not exist or is
* of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
public Boolean[] getBooleanArray(String key, Boolean[] defaultValue);
/**
* Put a number array in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
public boolean putNumberArray(String key, double[] value);
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doens't exist.
* @return False if the table key exists with a different type
*/
public boolean setDefaultNumberArray(String key, double[] defaultValue);
/**
* Put a number array in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
public boolean putNumberArray(String key, Double[] value);
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doens't exist.
* @return False if the table key exists with a different type
*/
public boolean setDefaultNumberArray(String key, Double[] defaultValue);
/**
* Returns the number array the key maps to.
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method {@link #getNumberArray(String, double[])}.
*/
@Deprecated
public double[] getNumberArray(String key) throws TableKeyNotDefinedException;
/**
* Returns the number array the key maps to. If the key does not exist or is
* of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
public double[] getNumberArray(String key, double[] defaultValue);
/**
* Returns the number array the key maps to. If the key does not exist or is
* of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
public Double[] getNumberArray(String key, Double[] defaultValue);
/**
* Put a string array in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
public boolean putStringArray(String key, String[] value);
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doens't exist.
* @return False if the table key exists with a different type
*/
public boolean setDefaultStringArray(String key, String[] defaultValue);
/**
* Returns the string array the key maps to.
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method {@link #getStringArray(String, String[])}.
*/
@Deprecated
public String[] getStringArray(String key) throws TableKeyNotDefinedException;
/**
* Returns the string array the key maps to. If the key does not exist or is
* of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
public String[] getStringArray(String key, String[] defaultValue);
/**
* Put a raw value (byte array) in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
public boolean putRaw(String key, byte[] value);
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doens't exist.
* @return False if the table key exists with a different type
*/
public boolean setDefaultRaw(String key, byte[] defaultValue);
/**
* Put a raw value (bytes from a byte buffer) in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @param len the length of the value
* @return False if the table key already exists with a different type
*/
public boolean putRaw(String key, ByteBuffer value, int len);
/**
* Returns the raw value (byte array) the key maps to.
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method {@link #getRaw(String, byte[])}.
*/
@Deprecated
public byte[] getRaw(String key) throws TableKeyNotDefinedException;
/**
* Returns the raw value (byte array) the key maps to. If the key does not
* exist or is of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
public byte[] getRaw(String key, byte[] defaultValue);
/** Notifier flag values. */
public static final int NOTIFY_IMMEDIATE = 0x01;
public static final int NOTIFY_LOCAL = 0x02;
public static final int NOTIFY_NEW = 0x04;
public static final int NOTIFY_DELETE = 0x08;
public static final int NOTIFY_UPDATE = 0x10;
public static final int NOTIFY_FLAGS = 0x20;
/**
* Add a listener for changes to the table
* @param listener the listener to add
*/
public void addTableListener(ITableListener listener);
/**
* Add a listener for changes to the table
* @param listener the listener to add
* @param immediateNotify if true then this listener will be notified of all
* current entries (marked as new)
*/
public void addTableListener(ITableListener listener,
boolean immediateNotify);
/**
* Add a listener for changes to the table
* @param listener the listener to add
* @param flags bitmask specifying desired notifications
*/
public void addTableListenerEx(ITableListener listener, int flags);
/**
* Add a listener for changes to a specific key the table
* @param key the key to listen for
* @param listener the listener to add
* @param immediateNotify if true then this listener will be notified of all
* current entries (marked as new)
*/
public void addTableListener(String key, ITableListener listener,
boolean immediateNotify);
/**
* Add a listener for changes to a specific key the table
* @param key the key to listen for
* @param listener the listener to add
* @param flags bitmask specifying desired notifications
*/
public void addTableListenerEx(String key, ITableListener listener,
int flags);
/**
* This will immediately notify the listener of all current sub tables
* @param listener the listener to notify
*/
public void addSubTableListener(final ITableListener listener);
/**
* This will immediately notify the listener of all current sub tables
* @param listener the listener to notify
* @param localNotify if true then this listener will be notified of all
* local changes in addition to all remote changes
*/
public void addSubTableListener(final ITableListener listener,
boolean localNotify);
/**
* Remove a listener from receiving table events
* @param listener the listener to be removed
*/
public void removeTableListener(ITableListener listener);
/*
* Deprecated Methods
*/
/**
* Maps the specified key to the specified value in this table.
* The key can not be null.
* The value can be retrieved by calling the get method with a key that is
* equal to the original key.
* @param key the key
* @param value the value
* @return False if the table key already exists with a different type
* @throws IllegalArgumentException if key is null
* @deprecated Use {@link #putNumber(String, double)} instead.
*/
@Deprecated
public boolean putInt(String key, int value);
/**
* Returns the value at the specified key.
* @param key the key
* @return the value
* @throws TableKeyNotDefinedException if there is no value mapped to by the
* key
* @throws IllegalArgumentException if the value mapped to by the key is not
* an int
* @throws IllegalArgumentException if the key is null
* @deprecated Use {@link #getNumber(String, double)} instead.
*/
@Deprecated
public int getInt(String key) throws TableKeyNotDefinedException;
/**
* Returns the value at the specified key.
* @param key the key
* @param defaultValue the value returned if the key is undefined
* @return the value
* @throws IllegalArgumentException if the value mapped to by the key is not
* an int
* @throws IllegalArgumentException if the key is null
* @deprecated Use {@link #getNumber(String, double)} instead.
*/
@Deprecated
public int getInt(String key, int defaultValue)
throws TableKeyNotDefinedException;
/**
* Maps the specified key to the specified value in this table.
* The key can not be null.
* The value can be retrieved by calling the get method with a key that is
* equal to the original key.
* @param key the key
* @param value the value
* @return False if the table key already exists with a different type
* @throws IllegalArgumentException if key is null
* @deprecated Use {@link #putNumber(String, double)} instead.
*/
@Deprecated
public boolean putDouble(String key, double value);
/**
* Returns the value at the specified key.
* @param key the key
* @return the value
* @throws TableKeyNotDefinedException if there is no
* value mapped to by the key
* @throws IllegalArgumentException if the value mapped to by the key is not a
* double
* @throws IllegalArgumentException if the key is null
* @deprecated Use {@link #getNumber(String, double)} instead.
*/
@Deprecated
public double getDouble(String key) throws TableKeyNotDefinedException;
/**
* Returns the value at the specified key.
* @param key the key
* @param defaultValue the value returned if the key is undefined
* @return the value
* @throws IllegalArgumentException if the value mapped to by the key is not a
* double
* @throws IllegalArgumentException if the key is null
* @deprecated Use {@link #getNumber(String, double)} instead.
*/
@Deprecated
public double getDouble(String key, double defaultValue);
/**
* Gets the full path of this table.
*/
public String getPath();
}

View File

@@ -0,0 +1,35 @@
package edu.wpi.first.wpilibj.tables;
/**
* A listener that listens to changes in values in a {@link ITable}
*
* @author Mitchell
*
*/
@FunctionalInterface
public interface ITableListener {
/**
* Called when a key-value pair is changed in a {@link ITable}
* @param source the table the key-value pair exists in
* @param key the key associated with the value that changed
* @param value the new value
* @param isNew true if the key did not previously exist in the table, otherwise it is false
*/
public void valueChanged(ITable source, String key, Object value, boolean isNew);
/**
* Extended version of valueChanged. Called when a key-value pair is
* changed in a {@link ITable}. The default implementation simply calls
* valueChanged(). If this is overridden, valueChanged() will not be
* called.
* @param source the table the key-value pair exists in
* @param key the key associated with the value that changed
* @param value the new value
* @param flags update flags; for example, NOTIFY_NEW if the key did not
* previously exist in the table
*/
default public void valueChangedEx(ITable source, String key, Object value, int flags) {
// NOTIFY_NEW = 0x04
valueChanged(source, key, value, (flags & 0x04) != 0);
}
}

View File

@@ -0,0 +1,20 @@
package edu.wpi.first.wpilibj.tables;
import java.util.NoSuchElementException;
/**
* An exception throw when the lookup a a key-value fails in a {@link ITable}
*
* @author Mitchell
*
*/
public class TableKeyNotDefinedException extends NoSuchElementException {
/**
* @param key the key that was not defined in the table
*/
public TableKeyNotDefinedException(String key) {
super("Unknown Table Key: "+key);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,572 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef NETWORKTABLE_H_
#define NETWORKTABLE_H_
#include <functional>
#include <mutex>
#include <vector>
#include "tables/ITable.h"
/**
* A network table that knows its subtable path.
*/
class NetworkTable : public ITable {
private:
struct private_init {};
std::string m_path;
std::mutex m_mutex;
typedef std::pair<ITableListener*, unsigned int> Listener;
std::vector<Listener> m_listeners;
static std::vector<std::string> s_ip_addresses;
static std::string s_persistent_filename;
static bool s_client;
static bool s_enable_ds;
static bool s_running;
static unsigned int s_port;
public:
NetworkTable(llvm::StringRef path, const private_init&);
virtual ~NetworkTable();
/**
* The path separator for sub-tables and keys
*
*/
static const char PATH_SEPARATOR_CHAR;
/**
* @throws IOException
*/
static void Initialize();
static void Shutdown();
/**
* set that network tables should be a client
* This must be called before initialize or GetTable
*/
static void SetClientMode();
/**
* set that network tables should be a server
* This must be called before initialize or GetTable
*/
static void SetServerMode();
/**
* set the team the robot is configured for (this will set the mdns address
* that network tables will connect to in client mode)
* This must be called before initialize or GetTable
* @param team the team number
*/
static void SetTeam(int team);
/**
* @param address the adress that network tables will connect to in client
* mode
*/
static void SetIPAddress(llvm::StringRef address);
/**
* @param addresses the addresses that network tables will connect to in
* client mode (in round robin order)
*/
static void SetIPAddress(llvm::ArrayRef<std::string> addresses);
/**
* @param port the port number that network tables will connect to in client
* mode or listen to in server mode
*/
static void SetPort(unsigned int port);
/**
* @param enabled whether to enable the connection to the local DS to get
* the robot IP address (defaults to enabled)
*/
static void SetDSClientEnabled(bool enabled);
/**
* Sets the persistent filename.
* @param filename the filename that the network tables server uses for
* automatic loading and saving of persistent values
*/
static void SetPersistentFilename(llvm::StringRef filename);
/**
* Sets the network identity.
* This is provided in the connection info on the remote end.
* @param name identity
*/
static void SetNetworkIdentity(llvm::StringRef name);
/**
* Deletes ALL keys in ALL subtables. Use with caution!
*/
static void GlobalDeleteAll();
/**
* Flushes all updated values immediately to the network.
* Note: This is rate-limited to protect the network from flooding.
* This is primarily useful for synchronizing network updates with
* user code.
*/
static void Flush();
/**
* Set the periodic update rate.
*
* @param interval update interval in seconds (range 0.01 to 1.0)
*/
static void SetUpdateRate(double interval);
/**
* Saves persistent keys to a file. The server does this automatically.
*
* @param filename file name
* @return Error (or nullptr).
*/
static const char* SavePersistent(llvm::StringRef filename);
/**
* Loads persistent keys from a file. The server does this automatically.
*
* @param filename file name
* @param warn callback function called for warnings
* @return Error (or nullptr).
*/
static const char* LoadPersistent(
llvm::StringRef filename,
std::function<void(size_t line, const char* msg)> warn);
/**
* Gets the table with the specified key. If the table does not exist, a new
* table will be created.<br>
* This will automatically initialize network tables if it has not been
* already.
*
* @param key
* the key name
* @return the network table requested
*/
static std::shared_ptr<NetworkTable> GetTable(llvm::StringRef key);
void AddTableListener(ITableListener* listener) override;
void AddTableListener(ITableListener* listener,
bool immediateNotify) override;
void AddTableListenerEx(ITableListener* listener,
unsigned int flags) override;
void AddTableListener(llvm::StringRef key, ITableListener* listener,
bool immediateNotify) override;
void AddTableListenerEx(llvm::StringRef key, ITableListener* listener,
unsigned int flags) override;
void AddSubTableListener(ITableListener* listener) override;
void AddSubTableListener(ITableListener* listener, bool localNotify) override;
void RemoveTableListener(ITableListener* listener) override;
/**
* Returns the table at the specified key. If there is no table at the
* specified key, it will create a new table
*
* @param key
* the key name
* @return the networktable to be returned
*/
std::shared_ptr<ITable> GetSubTable(llvm::StringRef key) const override;
/**
* Determines whether the given key is in this table.
*
* @param key the key to search for
* @return true if the table as a value assigned to the given key
*/
bool ContainsKey(llvm::StringRef key) const override;
/**
* Determines whether there exists a non-empty subtable for this key
* in this table.
*
* @param key the key to search for
* @return true if there is a subtable with the key which contains at least
* one key/subtable of its own
*/
bool ContainsSubTable(llvm::StringRef key) const override;
/**
* @param types bitmask of types; 0 is treated as a "don't care".
* @return keys currently in the table
*/
std::vector<std::string> GetKeys(int types = 0) const override;
/**
* @return subtables currently in the table
*/
std::vector<std::string> GetSubTables() const override;
/**
* Makes a key's value persistent through program restarts.
*
* @param key the key to make persistent
*/
void SetPersistent(llvm::StringRef key) override;
/**
* Stop making a key's value persistent through program restarts.
* The key cannot be null.
*
* @param key the key name
*/
void ClearPersistent(llvm::StringRef key) override;
/**
* Returns whether the value is persistent through program restarts.
* The key cannot be null.
*
* @param key the key name
*/
bool IsPersistent(llvm::StringRef key) const override;
/**
* Sets flags on the specified key in this table. The key can
* not be null.
*
* @param key the key name
* @param flags the flags to set (bitmask)
*/
void SetFlags(llvm::StringRef key, unsigned int flags) override;
/**
* Clears flags on the specified key in this table. The key can
* not be null.
*
* @param key the key name
* @param flags the flags to clear (bitmask)
*/
void ClearFlags(llvm::StringRef key, unsigned int flags) override;
/**
* Returns the flags for the specified key.
*
* @param key the key name
* @return the flags, or 0 if the key is not defined
*/
unsigned int GetFlags(llvm::StringRef key) const override;
/**
* Deletes the specified key in this table.
*
* @param key the key name
*/
void Delete(llvm::StringRef key) override;
/**
* Put a number in the table
*
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
bool PutNumber(llvm::StringRef key, double value) override;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultNumber(llvm::StringRef key,
double defaultValue) override;
/**
* Gets the number associated with the given name.
*
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method.
*/
WPI_DEPRECATED(
"Raises an exception if key not found; "
"use GetNumber(StringRef key, double defaultValue) instead")
virtual double GetNumber(llvm::StringRef key) const override;
/**
* Gets the number associated with the given name.
*
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
virtual double GetNumber(llvm::StringRef key,
double defaultValue) const override;
/**
* Put a string in the table
*
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutString(llvm::StringRef key, llvm::StringRef value) override;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultString(llvm::StringRef key,
llvm::StringRef defaultValue) override;
/**
* Gets the string associated with the given name.
*
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method.
*/
WPI_DEPRECATED(
"Raises an exception if key not found; "
"use GetString(StringRef key, StringRef defaultValue) instead")
virtual std::string GetString(llvm::StringRef key) const override;
/**
* Gets the string associated with the given name. If the key does not
* exist or is of different type, it will return the default value.
*
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
virtual std::string GetString(llvm::StringRef key,
llvm::StringRef defaultValue) const override;
/**
* Put a boolean in the table
*
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutBoolean(llvm::StringRef key, bool value) override;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultBoolean(llvm::StringRef key,
bool defaultValue) override;
/**
* Gets the boolean associated with the given name.
*
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method.
*/
WPI_DEPRECATED(
"Raises an exception if key not found; "
"use GetBoolean(StringRef key, bool defaultValue) instead")
virtual bool GetBoolean(llvm::StringRef key) const override;
/**
* Gets the boolean associated with the given name. If the key does not
* exist or is of different type, it will return the default value.
*
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
virtual bool GetBoolean(llvm::StringRef key,
bool defaultValue) const override;
/**
* Put a boolean array in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*
* @note The array must be of int's rather than of bool's because
* std::vector<bool> is special-cased in C++. 0 is false, any
* non-zero value is true.
*/
virtual bool PutBooleanArray(llvm::StringRef key,
llvm::ArrayRef<int> value) override;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultBooleanArray(
llvm::StringRef key, llvm::ArrayRef<int> defaultValue) override;
/**
* Returns the boolean array the key maps to. If the key does not exist or is
* of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*
* @note This makes a copy of the array. If the overhead of this is a
* concern, use GetValue() instead.
*
* @note The returned array is std::vector<int> instead of std::vector<bool>
* because std::vector<bool> is special-cased in C++. 0 is false, any
* non-zero value is true.
*/
virtual std::vector<int> GetBooleanArray(
llvm::StringRef key, llvm::ArrayRef<int> defaultValue) const override;
/**
* Put a number array in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutNumberArray(llvm::StringRef key,
llvm::ArrayRef<double> value) override;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultNumberArray(
llvm::StringRef key, llvm::ArrayRef<double> defaultValue) override;
/**
* Returns the number array the key maps to. If the key does not exist or is
* of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*
* @note This makes a copy of the array. If the overhead of this is a
* concern, use GetValue() instead.
*/
virtual std::vector<double> GetNumberArray(
llvm::StringRef key, llvm::ArrayRef<double> defaultValue) const override;
/**
* Put a string array in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutStringArray(llvm::StringRef key,
llvm::ArrayRef<std::string> value) override;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultStringArray(
llvm::StringRef key, llvm::ArrayRef<std::string> defaultValue) override;
/**
* Returns the string array the key maps to. If the key does not exist or is
* of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*
* @note This makes a copy of the array. If the overhead of this is a
* concern, use GetValue() instead.
*/
virtual std::vector<std::string> GetStringArray(
llvm::StringRef key,
llvm::ArrayRef<std::string> defaultValue) const override;
/**
* Put a raw value (byte array) in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutRaw(llvm::StringRef key, llvm::StringRef value) override;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultRaw(llvm::StringRef key,
llvm::StringRef defaultValue) override;
/**
* Returns the raw value (byte array) the key maps to. If the key does not
* exist or is of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*
* @note This makes a copy of the raw contents. If the overhead of this is a
* concern, use GetValue() instead.
*/
virtual std::string GetRaw(llvm::StringRef key,
llvm::StringRef defaultValue) const override;
/**
* Put a value in the table
*
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
bool PutValue(llvm::StringRef key, std::shared_ptr<nt::Value> value) override;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultValue(
llvm::StringRef key, std::shared_ptr<nt::Value> defaultValue) override;
/**
* Gets the value associated with a key as an object
*
* @param key the key of the value to look up
* @return the value associated with the given key, or nullptr if the key
* does not exist
*/
std::shared_ptr<nt::Value> GetValue(llvm::StringRef key) const override;
/**
* Gets the full path of this table.
*/
llvm::StringRef GetPath() const override;
};
#endif // NETWORKTABLE_H_

View File

@@ -0,0 +1,183 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef NT_VALUE_H_
#define NT_VALUE_H_
#include <cassert>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
#include "llvm/ArrayRef.h"
#include "llvm/StringRef.h"
#include "ntcore_c.h"
namespace nt {
using llvm::ArrayRef;
using llvm::StringRef;
/** NetworkTables Entry Value */
class Value {
struct private_init {};
public:
Value();
Value(NT_Type type, const private_init&);
~Value();
NT_Type type() const { return m_val.type; }
const NT_Value& value() const { return m_val; }
unsigned long long last_change() const { return m_val.last_change; }
/*
* Type Checkers
*/
bool IsBoolean() const { return m_val.type == NT_BOOLEAN; }
bool IsDouble() const { return m_val.type == NT_DOUBLE; }
bool IsString() const { return m_val.type == NT_STRING; }
bool IsRaw() const { return m_val.type == NT_RAW; }
bool IsRpc() const { return m_val.type == NT_RPC; }
bool IsBooleanArray() const { return m_val.type == NT_BOOLEAN_ARRAY; }
bool IsDoubleArray() const { return m_val.type == NT_DOUBLE_ARRAY; }
bool IsStringArray() const { return m_val.type == NT_STRING_ARRAY; }
/*
* Type-Safe Getters
*/
bool GetBoolean() const {
assert(m_val.type == NT_BOOLEAN);
return m_val.data.v_boolean != 0;
}
double GetDouble() const {
assert(m_val.type == NT_DOUBLE);
return m_val.data.v_double;
}
StringRef GetString() const {
assert(m_val.type == NT_STRING);
return m_string;
}
StringRef GetRaw() const {
assert(m_val.type == NT_RAW);
return m_string;
}
StringRef GetRpc() const {
assert(m_val.type == NT_RPC);
return m_string;
}
ArrayRef<int> GetBooleanArray() const {
assert(m_val.type == NT_BOOLEAN_ARRAY);
return ArrayRef<int>(m_val.data.arr_boolean.arr,
m_val.data.arr_boolean.size);
}
ArrayRef<double> GetDoubleArray() const {
assert(m_val.type == NT_DOUBLE_ARRAY);
return ArrayRef<double>(m_val.data.arr_double.arr,
m_val.data.arr_double.size);
}
ArrayRef<std::string> GetStringArray() const {
assert(m_val.type == NT_STRING_ARRAY);
return m_string_array;
}
static std::shared_ptr<Value> MakeBoolean(bool value) {
auto val = std::make_shared<Value>(NT_BOOLEAN, private_init());
val->m_val.data.v_boolean = value;
return val;
}
static std::shared_ptr<Value> MakeDouble(double value) {
auto val = std::make_shared<Value>(NT_DOUBLE, private_init());
val->m_val.data.v_double = value;
return val;
}
static std::shared_ptr<Value> MakeString(StringRef value) {
auto val = std::make_shared<Value>(NT_STRING, private_init());
val->m_string = value;
val->m_val.data.v_string.str = const_cast<char*>(val->m_string.c_str());
val->m_val.data.v_string.len = val->m_string.size();
return val;
}
#ifdef _MSC_VER
template <typename T,
typename = std::enable_if_t<std::is_same<T, std::string>>>
#else
template <typename T,
typename std::enable_if<std::is_same<T, std::string>::value>::type>
#endif
static std::shared_ptr<Value> MakeString(T&& value) {
auto val = std::make_shared<Value>(NT_STRING, private_init());
val->m_string = std::move(value);
val->m_val.data.v_string.str = const_cast<char*>(val->m_string.c_str());
val->m_val.data.v_string.len = val->m_string.size();
return val;
}
static std::shared_ptr<Value> MakeRaw(StringRef value) {
auto val = std::make_shared<Value>(NT_RAW, private_init());
val->m_string = value;
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
val->m_val.data.v_raw.len = val->m_string.size();
return val;
}
#ifdef _MSC_VER
template <typename T,
typename = std::enable_if_t<std::is_same<T, std::string>>>
#else
template <typename T,
typename std::enable_if<std::is_same<T, std::string>::value>::type>
#endif
static std::shared_ptr<Value> MakeRaw(T&& value) {
auto val = std::make_shared<Value>(NT_RAW, private_init());
val->m_string = std::move(value);
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
val->m_val.data.v_raw.len = val->m_string.size();
return val;
}
static std::shared_ptr<Value> MakeRpc(StringRef value) {
auto val = std::make_shared<Value>(NT_RPC, private_init());
val->m_string = value;
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
val->m_val.data.v_raw.len = val->m_string.size();
return val;
}
template <typename T>
static std::shared_ptr<Value> MakeRpc(T&& value) {
auto val = std::make_shared<Value>(NT_RPC, private_init());
val->m_string = std::move(value);
val->m_val.data.v_raw.str = const_cast<char*>(val->m_string.c_str());
val->m_val.data.v_raw.len = val->m_string.size();
return val;
}
static std::shared_ptr<Value> MakeBooleanArray(ArrayRef<int> value);
static std::shared_ptr<Value> MakeDoubleArray(ArrayRef<double> value);
static std::shared_ptr<Value> MakeStringArray(ArrayRef<std::string> value);
// Note: This function moves the values out of the vector.
static std::shared_ptr<Value> MakeStringArray(
std::vector<std::string>&& value);
Value(const Value&) = delete;
Value& operator=(const Value&) = delete;
friend bool operator==(const Value& lhs, const Value& rhs);
private:
NT_Value m_val;
std::string m_string;
std::vector<std::string> m_string_array;
};
bool operator==(const Value& lhs, const Value& rhs);
inline bool operator!=(const Value& lhs, const Value& rhs) {
return !(lhs == rhs);
}
} // namespace nt
#endif // NT_VALUE_H_

View File

@@ -0,0 +1,19 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef NTCORE_H_
#define NTCORE_H_
/* C API */
#include "ntcore_c.h"
#ifdef __cplusplus
/* C++ API */
#include "ntcore_cpp.h"
#endif /* __cplusplus */
#endif /* NTCORE_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,295 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef NTCORE_CPP_H_
#define NTCORE_CPP_H_
#include <cassert>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "llvm/ArrayRef.h"
#include "llvm/StringRef.h"
#include "nt_Value.h"
namespace nt {
using llvm::ArrayRef;
using llvm::StringRef;
/** NetworkTables Entry Information */
struct EntryInfo {
/** Entry name */
std::string name;
/** Entry type */
NT_Type type;
/** Entry flags */
unsigned int flags;
/** Timestamp of last change to entry (type or value). */
unsigned long long last_change;
};
/** NetworkTables Connection Information */
struct ConnectionInfo {
std::string remote_id;
std::string remote_ip;
unsigned int remote_port;
unsigned long long last_update;
unsigned int protocol_version;
};
/** NetworkTables RPC Parameter Definition */
struct RpcParamDef {
RpcParamDef() = default;
RpcParamDef(StringRef name_, std::shared_ptr<Value> def_value_)
: name(name_), def_value(def_value_) {}
std::string name;
std::shared_ptr<Value> def_value;
};
/** NetworkTables RPC Result Definition */
struct RpcResultDef {
RpcResultDef() = default;
RpcResultDef(StringRef name_, NT_Type type_) : name(name_), type(type_) {}
std::string name;
NT_Type type;
};
/** NetworkTables RPC Definition */
struct RpcDefinition {
unsigned int version;
std::string name;
std::vector<RpcParamDef> params;
std::vector<RpcResultDef> results;
};
/** NetworkTables RPC Call Data */
struct RpcCallInfo {
unsigned int rpc_id;
unsigned int call_uid;
std::string name;
std::string params;
};
/*
* Table Functions
*/
/** Get Entry Value.
* Returns copy of current entry value.
* Note that one of the type options is "unassigned".
*
* @param name entry name (UTF-8 string)
* @return entry value
*/
std::shared_ptr<Value> GetEntryValue(StringRef name);
/** Set Default Entry Value
* Returns copy of current entry value if it exists.
* Otherwise, sets passed in value, and returns set value.
* Note that one of the type options is "unassigned".
*
* @param name entry name (UTF-8 string)
* @param value value to be set if name does not exist
* @return False on error (value not set), True on success
*/
bool SetDefaultEntryValue(StringRef name, std::shared_ptr<Value> value);
/** Set Entry Value.
* Sets new entry value. If type of new value differs from the type of the
* currently stored entry, returns error and does not update value.
*
* @param name entry name (UTF-8 string)
* @param value new entry value
* @return False on error (type mismatch), True on success
*/
bool SetEntryValue(StringRef name, std::shared_ptr<Value> value);
/** Set Entry Type and Value.
* Sets new entry value. If type of new value differs from the type of the
* currently stored entry, the currently stored entry type is overridden
* (generally this will generate an Entry Assignment message).
*
* This is NOT the preferred method to update a value; generally
* SetEntryValue() should be used instead, with appropriate error handling.
*
* @param name entry name (UTF-8 string)
* @param value new entry value
*/
void SetEntryTypeValue(StringRef name, std::shared_ptr<Value> value);
/** Set Entry Flags.
*/
void SetEntryFlags(StringRef name, unsigned int flags);
/** Get Entry Flags.
*/
unsigned int GetEntryFlags(StringRef name);
/** Delete Entry.
* Deletes an entry. This is a new feature in version 3.0 of the protocol,
* so this may not have an effect if any other node in the network is not
* version 3.0 or newer.
*
* Note: GetConnections() can be used to determine the protocol version
* of direct remote connection(s), but this is not sufficient to determine
* if all nodes in the network are version 3.0 or newer.
*
* @param name entry name (UTF-8 string)
*/
void DeleteEntry(StringRef name);
/** Delete All Entries.
* Deletes ALL table entries. This is a new feature in version 3.0 of the
* so this may not have an effect if any other node in the network is not
* version 3.0 or newer.
*
* Note: GetConnections() can be used to determine the protocol version
* of direct remote connection(s), but this is not sufficient to determine
* if all nodes in the network are version 3.0 or newer.
*/
void DeleteAllEntries();
/** Get Entry Information.
* Returns an array of entry information (name, entry type,
* and timestamp of last change to type/value). The results are optionally
* filtered by string prefix and entry type to only return a subset of all
* entries.
*
* @param prefix entry name required prefix; only entries whose name
* starts with this string are returned
* @param types bitmask of NT_Type values; 0 is treated specially
* as a "don't care"
* @return Array of entry information.
*/
std::vector<EntryInfo> GetEntryInfo(StringRef prefix, unsigned int types);
/** Flush Entries.
* Forces an immediate flush of all local entry changes to network.
* Normally this is done on a regularly scheduled interval (see
* NT_SetUpdateRate()).
*
* Note: flushes are rate limited to avoid excessive network traffic. If
* the time between calls is too short, the flush will occur after the minimum
* time elapses (rather than immediately).
*/
void Flush();
/*
* Callback Creation Functions
*/
void SetListenerOnStart(std::function<void()> on_start);
void SetListenerOnExit(std::function<void()> on_exit);
typedef std::function<void(unsigned int uid, StringRef name,
std::shared_ptr<Value> value, unsigned int flags)>
EntryListenerCallback;
typedef std::function<void(unsigned int uid, bool connected,
const ConnectionInfo& conn)>
ConnectionListenerCallback;
unsigned int AddEntryListener(StringRef prefix, EntryListenerCallback callback,
unsigned int flags);
void RemoveEntryListener(unsigned int entry_listener_uid);
unsigned int AddConnectionListener(ConnectionListenerCallback callback,
bool immediate_notify);
void RemoveConnectionListener(unsigned int conn_listener_uid);
bool NotifierDestroyed();
/*
* Remote Procedure Call Functions
*/
#if defined(_MSC_VER) && _MSC_VER < 1900
const double kTimeout_Indefinite = -1;
#else
constexpr double kTimeout_Indefinite = -1;
#endif
void SetRpcServerOnStart(std::function<void()> on_start);
void SetRpcServerOnExit(std::function<void()> on_exit);
typedef std::function<std::string(StringRef name, StringRef params,
const ConnectionInfo& conn_info)>
RpcCallback;
void CreateRpc(StringRef name, StringRef def, RpcCallback callback);
void CreatePolledRpc(StringRef name, StringRef def);
bool PollRpc(bool blocking, RpcCallInfo* call_info);
bool PollRpc(bool blocking, double time_out, RpcCallInfo* call_info);
void PostRpcResponse(unsigned int rpc_id, unsigned int call_uid,
StringRef result);
unsigned int CallRpc(StringRef name, StringRef params);
bool GetRpcResult(bool blocking, unsigned int call_uid, std::string* result);
bool GetRpcResult(bool blocking, unsigned int call_uid, double time_out,
std::string* result);
void CancelBlockingRpcResult(unsigned int call_uid);
std::string PackRpcDefinition(const RpcDefinition& def);
bool UnpackRpcDefinition(StringRef packed, RpcDefinition* def);
std::string PackRpcValues(ArrayRef<std::shared_ptr<Value>> values);
std::vector<std::shared_ptr<Value>> UnpackRpcValues(StringRef packed,
ArrayRef<NT_Type> types);
/*
* Client/Server Functions
*/
void SetNetworkIdentity(StringRef name);
unsigned int GetNetworkMode();
void StartServer(StringRef persist_filename, const char* listen_address,
unsigned int port);
void StopServer();
void StartClient();
void StartClient(const char* server_name, unsigned int port);
void StartClient(ArrayRef<std::pair<StringRef, unsigned int>> servers);
void StopClient();
void SetServer(const char* server_name, unsigned int port);
void SetServer(ArrayRef<std::pair<StringRef, unsigned int>> servers);
void StartDSClient(unsigned int port);
void StopDSClient();
void StopRpcServer();
void StopNotifier();
void SetUpdateRate(double interval);
std::vector<ConnectionInfo> GetConnections();
/*
* Persistent Functions
*/
/* return error string, or nullptr if successful */
const char* SavePersistent(StringRef filename);
const char* LoadPersistent(
StringRef filename, std::function<void(size_t line, const char* msg)> warn);
/*
* Utility Functions
*/
/* timestamp */
unsigned long long Now();
/* logging */
typedef std::function<void(unsigned int level, const char* file,
unsigned int line, const char* msg)>
LogFunc;
void SetLogger(LogFunc func, unsigned int min_level);
} // namespace nt
#endif /* NTCORE_CPP_H_ */

View File

@@ -0,0 +1,84 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2016. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef NTCORE_TEST_H_
#define NTCORE_TEST_H_
#include "ntcore.h"
// Functions in this header are to be used only for testing
extern "C" {
struct NT_String* NT_GetStringForTesting(const char* string, int* struct_size);
// No need for free as one already exists in main library
struct NT_EntryInfo* NT_GetEntryInfoForTesting(const char* name,
enum NT_Type type,
unsigned int flags,
unsigned long long last_change,
int* struct_size);
void NT_FreeEntryInfoForTesting(struct NT_EntryInfo* info);
struct NT_ConnectionInfo* NT_GetConnectionInfoForTesting(
const char* remote_id, const char* remote_ip, unsigned int remote_port,
unsigned long long last_update, unsigned int protocol_version,
int* struct_size);
void NT_FreeConnectionInfoForTesting(struct NT_ConnectionInfo* info);
struct NT_Value* NT_GetValueBooleanForTesting(unsigned long long last_change,
int val, int* struct_size);
struct NT_Value* NT_GetValueDoubleForTesting(unsigned long long last_change,
double val, int* struct_size);
struct NT_Value* NT_GetValueStringForTesting(unsigned long long last_change,
const char* str, int* struct_size);
struct NT_Value* NT_GetValueRawForTesting(unsigned long long last_change,
const char* raw, int raw_len,
int* struct_size);
struct NT_Value* NT_GetValueBooleanArrayForTesting(
unsigned long long last_change, const int* arr, size_t array_len,
int* struct_size);
struct NT_Value* NT_GetValueDoubleArrayForTesting(
unsigned long long last_change, const double* arr, size_t array_len,
int* struct_size);
struct NT_Value* NT_GetValueStringArrayForTesting(
unsigned long long last_change, const struct NT_String* arr,
size_t array_len, int* struct_size);
// No need for free as one already exists in the main library
struct NT_RpcParamDef* NT_GetRpcParamDefForTesting(const char* name,
const struct NT_Value* val,
int* struct_size);
void NT_FreeRpcParamDefForTesting(struct NT_RpcParamDef* def);
struct NT_RpcResultDef* NT_GetRpcResultsDefForTesting(const char* name,
enum NT_Type type,
int* struct_size);
void NT_FreeRpcResultsDefForTesting(struct NT_RpcResultDef* def);
struct NT_RpcDefinition* NT_GetRpcDefinitionForTesting(
unsigned int version, const char* name, size_t num_params,
const struct NT_RpcParamDef* params, size_t num_results,
const struct NT_RpcResultDef* results, int* struct_size);
// No need for free as one already exists in the main library
struct NT_RpcCallInfo* NT_GetRpcCallInfoForTesting(
unsigned int rpc_id, unsigned int call_uid, const char* name,
const char* params, size_t params_len, int* struct_size);
// No need for free as one already exists in the main library
}
#endif /* NTCORE_TEST_H_ */

View File

@@ -0,0 +1,493 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef ITABLE_H_
#define ITABLE_H_
#include <memory>
#include "llvm/StringRef.h"
#include "nt_Value.h"
#include "support/deprecated.h"
class ITableListener;
/**
* A table whose values can be read and written to
*/
class ITable {
public:
/**
* Determines whether the given key is in this table.
*
* @param key the key to search for
* @return true if the table as a value assigned to the given key
*/
virtual bool ContainsKey(llvm::StringRef key) const = 0;
/**
* Determines whether there exists a non-empty subtable for this key
* in this table.
*
* @param key the key to search for
* @return true if there is a subtable with the key which contains at least
* one key/subtable of its own
*/
virtual bool ContainsSubTable(llvm::StringRef key) const = 0;
/**
* Gets the subtable in this table for the given name.
*
* @param key the name of the table relative to this one
* @return a sub table relative to this one
*/
virtual std::shared_ptr<ITable> GetSubTable(llvm::StringRef key) const = 0;
/**
* @param types bitmask of types; 0 is treated as a "don't care".
* @return keys currently in the table
*/
virtual std::vector<std::string> GetKeys(int types = 0) const = 0;
/**
* @return subtables currently in the table
*/
virtual std::vector<std::string> GetSubTables() const = 0;
/**
* Makes a key's value persistent through program restarts.
*
* @param key the key to make persistent
*/
virtual void SetPersistent(llvm::StringRef key) = 0;
/**
* Stop making a key's value persistent through program restarts.
* The key cannot be null.
*
* @param key the key name
*/
virtual void ClearPersistent(llvm::StringRef key) = 0;
/**
* Returns whether the value is persistent through program restarts.
* The key cannot be null.
*
* @param key the key name
*/
virtual bool IsPersistent(llvm::StringRef key) const = 0;
/**
* Sets flags on the specified key in this table. The key can
* not be null.
*
* @param key the key name
* @param flags the flags to set (bitmask)
*/
virtual void SetFlags(llvm::StringRef key, unsigned int flags) = 0;
/**
* Clears flags on the specified key in this table. The key can
* not be null.
*
* @param key the key name
* @param flags the flags to clear (bitmask)
*/
virtual void ClearFlags(llvm::StringRef key, unsigned int flags) = 0;
/**
* Returns the flags for the specified key.
*
* @param key the key name
* @return the flags, or 0 if the key is not defined
*/
virtual unsigned int GetFlags(llvm::StringRef key) const = 0;
/**
* Deletes the specified key in this table.
*
* @param key the key name
*/
virtual void Delete(llvm::StringRef key) = 0;
/**
* Gets the value associated with a key as an object
*
* @param key the key of the value to look up
* @return the value associated with the given key, or nullptr if the key
* does not exist
*/
virtual std::shared_ptr<nt::Value> GetValue(llvm::StringRef key) const = 0;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultValue(llvm::StringRef key,
std::shared_ptr<nt::Value> defaultValue) = 0;
/**
* Put a value in the table
*
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutValue(llvm::StringRef key,
std::shared_ptr<nt::Value> value) = 0;
/**
* Put a number in the table
*
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutNumber(llvm::StringRef key, double value) = 0;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultNumber(llvm::StringRef key, double defaultValue) = 0;
/**
* Gets the number associated with the given name.
*
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method.
*/
WPI_DEPRECATED(
"Raises an exception if key not found; "
"use GetNumber(StringRef key, double defaultValue) instead")
virtual double GetNumber(llvm::StringRef key) const = 0;
/**
* Gets the number associated with the given name.
*
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
virtual double GetNumber(llvm::StringRef key, double defaultValue) const = 0;
/**
* Put a string in the table
*
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutString(llvm::StringRef key, llvm::StringRef value) = 0;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultString(llvm::StringRef key,
llvm::StringRef defaultValue) = 0;
/**
* Gets the string associated with the given name.
*
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method.
*/
WPI_DEPRECATED(
"Raises an exception if key not found; "
"use GetString(StringRef key, StringRef defaultValue) instead")
virtual std::string GetString(llvm::StringRef key) const = 0;
/**
* Gets the string associated with the given name. If the key does not
* exist or is of different type, it will return the default value.
*
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*
* @note This makes a copy of the string. If the overhead of this is a
* concern, use GetValue() instead.
*/
virtual std::string GetString(llvm::StringRef key,
llvm::StringRef defaultValue) const = 0;
/**
* Put a boolean in the table
*
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutBoolean(llvm::StringRef key, bool value) = 0;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultBoolean(llvm::StringRef key, bool defaultValue) = 0;
/**
* Gets the boolean associated with the given name.
*
* @param key the key to look up
* @return the value associated with the given key
* @throws TableKeyNotDefinedException if there is no value associated with
* the given key
* @deprecated This exception-raising method has been replaced by the
* default-taking method.
*/
WPI_DEPRECATED(
"Raises an exception if key not found; "
"use GetBoolean(StringRef key, bool defaultValue) instead")
virtual bool GetBoolean(llvm::StringRef key) const = 0;
/**
* Gets the boolean associated with the given name. If the key does not
* exist or is of different type, it will return the default value.
*
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*/
virtual bool GetBoolean(llvm::StringRef key, bool defaultValue) const = 0;
/**
* Put a boolean array in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*
* @note The array must be of int's rather than of bool's because
* std::vector<bool> is special-cased in C++. 0 is false, any
* non-zero value is true.
*/
virtual bool PutBooleanArray(llvm::StringRef key,
llvm::ArrayRef<int> value) = 0;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultBooleanArray(llvm::StringRef key,
llvm::ArrayRef<int> defaultValue) = 0;
/**
* Returns the boolean array the key maps to. If the key does not exist or is
* of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*
* @note This makes a copy of the array. If the overhead of this is a
* concern, use GetValue() instead.
*
* @note The returned array is std::vector<int> instead of std::vector<bool>
* because std::vector<bool> is special-cased in C++. 0 is false, any
* non-zero value is true.
*/
virtual std::vector<int> GetBooleanArray(
llvm::StringRef key, llvm::ArrayRef<int> defaultValue) const = 0;
/**
* Put a number array in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutNumberArray(llvm::StringRef key,
llvm::ArrayRef<double> value) = 0;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultNumberArray(llvm::StringRef key,
llvm::ArrayRef<double> defaultValue) = 0;
/**
* Returns the number array the key maps to. If the key does not exist or is
* of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*
* @note This makes a copy of the array. If the overhead of this is a
* concern, use GetValue() instead.
*/
virtual std::vector<double> GetNumberArray(
llvm::StringRef key, llvm::ArrayRef<double> defaultValue) const = 0;
/**
* Put a string array in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutStringArray(llvm::StringRef key,
llvm::ArrayRef<std::string> value) = 0;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultStringArray(
llvm::StringRef key, llvm::ArrayRef<std::string> defaultValue) = 0;
/**
* Returns the string array the key maps to. If the key does not exist or is
* of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*
* @note This makes a copy of the array. If the overhead of this is a
* concern, use GetValue() instead.
*/
virtual std::vector<std::string> GetStringArray(
llvm::StringRef key, llvm::ArrayRef<std::string> defaultValue) const = 0;
/**
* Put a raw value (byte array) in the table
* @param key the key to be assigned to
* @param value the value that will be assigned
* @return False if the table key already exists with a different type
*/
virtual bool PutRaw(llvm::StringRef key, llvm::StringRef value) = 0;
/**
* Gets the current value in the table, setting it if it does not exist.
* @param key the key
* @param defaultValue the default value to set if key doesn't exist.
* @returns False if the table key exists with a different type
*/
virtual bool SetDefaultRaw(llvm::StringRef key,
llvm::StringRef defaultValue) = 0;
/**
* Returns the raw value (byte array) the key maps to. If the key does not
* exist or is of different type, it will return the default value.
* @param key the key to look up
* @param defaultValue the value to be returned if no value is found
* @return the value associated with the given key or the given default value
* if there is no value associated with the key
*
* @note This makes a copy of the raw contents. If the overhead of this is a
* concern, use GetValue() instead.
*/
virtual std::string GetRaw(llvm::StringRef key,
llvm::StringRef defaultValue) const = 0;
/**
* Add a listener for changes to the table
*
* @param listener the listener to add
*/
virtual void AddTableListener(ITableListener* listener) = 0;
/**
* Add a listener for changes to the table
*
* @param listener the listener to add
* @param immediateNotify if true then this listener will be notified of all
* current entries (marked as new)
*/
virtual void AddTableListener(ITableListener* listener,
bool immediateNotify) = 0;
/**
* Add a listener for changes to the table
*
* @param listener the listener to add
* @param immediateNotify if true then this listener will be notified of all
* current entries (marked as new)
* @param flags bitmask of NT_NotifyKind specifying desired notifications
*/
virtual void AddTableListenerEx(ITableListener* listener,
unsigned int flags) = 0;
/**
* Add a listener for changes to a specific key the table
*
* @param key the key to listen for
* @param listener the listener to add
* @param immediateNotify if true then this listener will be notified of all
* current entries (marked as new)
*/
virtual void AddTableListener(llvm::StringRef key, ITableListener* listener,
bool immediateNotify) = 0;
/**
* Add a listener for changes to a specific key the table
*
* @param key the key to listen for
* @param listener the listener to add
* @param immediateNotify if true then this listener will be notified of all
* current entries (marked as new)
* @param flags bitmask of NT_NotifyKind specifying desired notifications
*/
virtual void AddTableListenerEx(llvm::StringRef key, ITableListener* listener,
unsigned int flags) = 0;
/**
* This will immediately notify the listener of all current sub tables
* @param listener the listener to add
*/
virtual void AddSubTableListener(ITableListener* listener) = 0;
/**
* This will immediately notify the listener of all current sub tables
* @param listener the listener to add
* @param localNotify if true then this listener will be notified of all
* local changes in addition to all remote changes
*/
virtual void AddSubTableListener(ITableListener* listener,
bool localNotify) = 0;
/**
* Remove a listener from receiving table events
*
* @param listener the listener to be removed
*/
virtual void RemoveTableListener(ITableListener* listener) = 0;
/**
* Gets the full path of this table.
*/
virtual llvm::StringRef GetPath() const = 0;
};
#endif // ITABLE_H_

View File

@@ -0,0 +1,47 @@
/*
* ITableListener.h
*/
#ifndef ITABLELISTENER_H_
#define ITABLELISTENER_H_
#include <memory>
#include "llvm/StringRef.h"
#include "nt_Value.h"
class ITable;
/**
* A listener that listens to changes in values in a {@link ITable}
*/
class ITableListener {
public:
virtual ~ITableListener() = default;
/**
* Called when a key-value pair is changed in a {@link ITable}
* @param source the table the key-value pair exists in
* @param key the key associated with the value that changed
* @param value the new value
* @param isNew true if the key did not previously exist in the table,
* otherwise it is false
*/
virtual void ValueChanged(ITable* source, llvm::StringRef key,
std::shared_ptr<nt::Value> value, bool isNew) = 0;
/**
* Extended version of ValueChanged. Called when a key-value pair is
* changed in a {@link ITable}. The default implementation simply calls
* ValueChanged(). If this is overridden, ValueChanged() will not be called.
* @param source the table the key-value pair exists in
* @param key the key associated with the value that changed
* @param value the new value
* @param flags update flags; for example, NT_NOTIFY_NEW if the key did not
* previously exist in the table
*/
virtual void ValueChangedEx(ITable* source, llvm::StringRef key,
std::shared_ptr<nt::Value> value,
unsigned int flags);
};
#endif /* ITABLELISTENER_H_ */

View File

@@ -0,0 +1,36 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef TABLEKEYNOTDEFINEDEXCEPTION_H_
#define TABLEKEYNOTDEFINEDEXCEPTION_H_
#include <exception>
#include "llvm/StringRef.h"
#if defined(_MSC_VER)
#define NT_NOEXCEPT throw()
#else
#define NT_NOEXCEPT noexcept
#endif
/**
* An exception thrown when the lookup a a key-value fails in a {@link ITable}
*/
class TableKeyNotDefinedException : public std::exception {
public:
/**
* @param key the key that was not defined in the table
*/
TableKeyNotDefinedException(llvm::StringRef key);
~TableKeyNotDefinedException() NT_NOEXCEPT;
const char* what() const NT_NOEXCEPT override;
private:
std::string msg;
};
#endif // TABLEKEYNOTDEFINEDEXCEPTION_H_

View File

@@ -0,0 +1,11 @@
package edu.wpi.first.wpilibj.networktables;
import org.junit.Test;
public class JNITest {
@Test
public void jniLinkTest() {
// Test to verify that the JNI test link works correctly.
edu.wpi.first.wpilibj.networktables.NetworkTablesJNI.flush();
}
}

View File

@@ -0,0 +1,27 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "networktables/NetworkTable.h"
#include "gtest/gtest.h"
class NetworkTableTest : public ::testing::Test {};
TEST_F(NetworkTableTest, ContainsKey) {
auto nt = NetworkTable::GetTable("containskey");
ASSERT_FALSE(nt->ContainsKey("testkey"));
nt->PutNumber("testkey", 5);
ASSERT_TRUE(nt->ContainsKey("testkey"));
}
TEST_F(NetworkTableTest, LeadingSlash) {
auto nt = NetworkTable::GetTable("leadingslash");
auto nt2 = NetworkTable::GetTable("/leadingslash");
ASSERT_FALSE(nt->ContainsKey("testkey"));
nt2->PutNumber("testkey", 5);
ASSERT_TRUE(nt->ContainsKey("testkey"));
}

View File

@@ -0,0 +1,873 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "Storage.h"
#include "StorageTest.h"
#include <sstream>
#include "gtest/gtest.h"
#include "gmock/gmock.h"
namespace nt {
class StorageTestEmpty : public StorageTest,
public ::testing::TestWithParam<bool> {
public:
StorageTestEmpty() { HookOutgoing(GetParam()); }
};
class StorageTestPopulateOne : public StorageTestEmpty {
public:
StorageTestPopulateOne() {
storage.SetEntryTypeValue("foo", Value::MakeBoolean(true));
outgoing.clear();
}
};
class StorageTestPopulated : public StorageTestEmpty {
public:
StorageTestPopulated() {
storage.SetEntryTypeValue("foo", Value::MakeBoolean(true));
storage.SetEntryTypeValue("foo2", Value::MakeDouble(0.0));
storage.SetEntryTypeValue("bar", Value::MakeDouble(1.0));
storage.SetEntryTypeValue("bar2", Value::MakeBoolean(false));
outgoing.clear();
}
};
class StorageTestPersistent : public StorageTestEmpty {
public:
StorageTestPersistent() {
storage.SetEntryTypeValue("boolean/true", Value::MakeBoolean(true));
storage.SetEntryTypeValue("boolean/false", Value::MakeBoolean(false));
storage.SetEntryTypeValue("double/neg", Value::MakeDouble(-1.5));
storage.SetEntryTypeValue("double/zero", Value::MakeDouble(0.0));
storage.SetEntryTypeValue("double/big", Value::MakeDouble(1.3e8));
storage.SetEntryTypeValue("string/empty", Value::MakeString(""));
storage.SetEntryTypeValue("string/normal", Value::MakeString("hello"));
storage.SetEntryTypeValue("string/special",
Value::MakeString(StringRef("\0\3\5\n", 4)));
storage.SetEntryTypeValue("raw/empty", Value::MakeRaw(""));
storage.SetEntryTypeValue("raw/normal", Value::MakeRaw("hello"));
storage.SetEntryTypeValue("raw/special",
Value::MakeRaw(StringRef("\0\3\5\n", 4)));
storage.SetEntryTypeValue("booleanarr/empty",
Value::MakeBooleanArray(std::vector<int>{}));
storage.SetEntryTypeValue("booleanarr/one",
Value::MakeBooleanArray(std::vector<int>{1}));
storage.SetEntryTypeValue("booleanarr/two",
Value::MakeBooleanArray(std::vector<int>{1, 0}));
storage.SetEntryTypeValue("doublearr/empty",
Value::MakeDoubleArray(std::vector<double>{}));
storage.SetEntryTypeValue("doublearr/one",
Value::MakeDoubleArray(std::vector<double>{0.5}));
storage.SetEntryTypeValue(
"doublearr/two",
Value::MakeDoubleArray(std::vector<double>{0.5, -0.25}));
storage.SetEntryTypeValue(
"stringarr/empty", Value::MakeStringArray(std::vector<std::string>{}));
storage.SetEntryTypeValue(
"stringarr/one",
Value::MakeStringArray(std::vector<std::string>{"hello"}));
storage.SetEntryTypeValue(
"stringarr/two",
Value::MakeStringArray(std::vector<std::string>{"hello", "world\n"}));
storage.SetEntryTypeValue(StringRef("\0\3\5\n", 4),
Value::MakeBoolean(true));
storage.SetEntryTypeValue("=", Value::MakeBoolean(true));
outgoing.clear();
}
};
class MockLoadWarn {
public:
MOCK_METHOD2(Warn, void(std::size_t line, llvm::StringRef msg));
};
TEST_P(StorageTestEmpty, Construct) {
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
}
TEST_P(StorageTestEmpty, StorageEntryInit) {
auto entry = GetEntry("foo");
EXPECT_FALSE(entry->value);
EXPECT_EQ(0u, entry->flags);
EXPECT_EQ("foobar", entry->name); // since GetEntry uses the tmp_entry.
EXPECT_EQ(0xffffu, entry->id);
EXPECT_EQ(SequenceNumber(), entry->seq_num);
}
TEST_P(StorageTestEmpty, GetEntryValueNotExist) {
EXPECT_FALSE(storage.GetEntryValue("foo"));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, GetEntryValueExist) {
auto value = Value::MakeBoolean(true);
storage.SetEntryTypeValue("foo", value);
outgoing.clear();
EXPECT_EQ(value, storage.GetEntryValue("foo"));
}
TEST_P(StorageTestEmpty, SetEntryTypeValueAssignNew) {
// brand new entry
auto value = Value::MakeBoolean(true);
storage.SetEntryTypeValue("foo", value);
EXPECT_EQ(value, GetEntry("foo")->value);
if (GetParam()) {
ASSERT_EQ(1u, idmap().size());
EXPECT_EQ(value, idmap()[0]->value);
} else {
EXPECT_TRUE(idmap().empty());
}
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryAssign, msg->type());
EXPECT_EQ("foo", msg->str());
if (GetParam())
EXPECT_EQ(0u, msg->id()); // assigned as server
else
EXPECT_EQ(0xffffu, msg->id()); // not assigned as client
EXPECT_EQ(1u, msg->seq_num_uid());
EXPECT_EQ(value, msg->value());
EXPECT_EQ(0u, msg->flags());
}
TEST_P(StorageTestPopulateOne, SetEntryTypeValueAssignTypeChange) {
// update with different type results in assignment message
auto value = Value::MakeDouble(0.0);
storage.SetEntryTypeValue("foo", value);
EXPECT_EQ(value, GetEntry("foo")->value);
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryAssign, msg->type());
EXPECT_EQ("foo", msg->str());
if (GetParam())
EXPECT_EQ(0u, msg->id()); // assigned as server
else
EXPECT_EQ(0xffffu, msg->id()); // not assigned as client
EXPECT_EQ(2u, msg->seq_num_uid()); // incremented
EXPECT_EQ(value, msg->value());
EXPECT_EQ(0u, msg->flags());
}
TEST_P(StorageTestPopulateOne, SetEntryTypeValueEqualValue) {
// update with same type and same value: change value contents but no update
// message is issued (minimizing bandwidth usage)
auto value = Value::MakeBoolean(true);
storage.SetEntryTypeValue("foo", value);
EXPECT_EQ(value, GetEntry("foo")->value);
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulated, SetEntryTypeValueDifferentValue) {
// update with same type and different value results in value update message
auto value = Value::MakeDouble(1.0);
storage.SetEntryTypeValue("foo2", value);
EXPECT_EQ(value, GetEntry("foo2")->value);
if (GetParam()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(2u, msg->seq_num_uid()); // incremented
EXPECT_EQ(value, msg->value());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value()); // still should be incremented
}
}
TEST_P(StorageTestEmpty, SetEntryTypeValueEmptyName) {
auto value = Value::MakeBoolean(true);
storage.SetEntryTypeValue("", value);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetEntryTypeValueEmptyValue) {
storage.SetEntryTypeValue("foo", nullptr);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetEntryValueAssignNew) {
// brand new entry
auto value = Value::MakeBoolean(true);
EXPECT_TRUE(storage.SetEntryValue("foo", value));
EXPECT_EQ(value, GetEntry("foo")->value);
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryAssign, msg->type());
EXPECT_EQ("foo", msg->str());
if (GetParam())
EXPECT_EQ(0u, msg->id()); // assigned as server
else
EXPECT_EQ(0xffffu, msg->id()); // not assigned as client
EXPECT_EQ(0u, msg->seq_num_uid());
EXPECT_EQ(value, msg->value());
EXPECT_EQ(0u, msg->flags());
}
TEST_P(StorageTestPopulateOne, SetEntryValueAssignTypeChange) {
// update with different type results in error and no message
auto value = Value::MakeDouble(0.0);
EXPECT_FALSE(storage.SetEntryValue("foo", value));
auto entry = GetEntry("foo");
EXPECT_NE(value, entry->value);
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulateOne, SetEntryValueEqualValue) {
// update with same type and same value: change value contents but no update
// message is issued (minimizing bandwidth usage)
auto value = Value::MakeBoolean(true);
EXPECT_TRUE(storage.SetEntryValue("foo", value));
auto entry = GetEntry("foo");
EXPECT_EQ(value, entry->value);
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulated, SetEntryValueDifferentValue) {
// update with same type and different value results in value update message
auto value = Value::MakeDouble(1.0);
EXPECT_TRUE(storage.SetEntryValue("foo2", value));
auto entry = GetEntry("foo2");
EXPECT_EQ(value, entry->value);
if (GetParam()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(2u, msg->seq_num_uid()); // incremented
EXPECT_EQ(value, msg->value());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value()); // still should be incremented
}
}
TEST_P(StorageTestEmpty, SetEntryValueEmptyName) {
auto value = Value::MakeBoolean(true);
EXPECT_TRUE(storage.SetEntryValue("", value));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetEntryValueEmptyValue) {
EXPECT_TRUE(storage.SetEntryValue("foo", nullptr));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetDefaultEntryAssignNew) {
// brand new entry
auto value = Value::MakeBoolean(true);
auto ret_val = storage.SetDefaultEntryValue("foo", value);
EXPECT_TRUE(ret_val);
EXPECT_EQ(value, GetEntry("foo")->value);
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryAssign, msg->type());
EXPECT_EQ("foo", msg->str());
if (GetParam())
EXPECT_EQ(0u, msg->id()); // assigned as server
else
EXPECT_EQ(0xffffu, msg->id()); // not assigned as client
EXPECT_EQ(0u, msg->seq_num_uid());
EXPECT_EQ(value, msg->value());
EXPECT_EQ(0u, msg->flags());
}
TEST_P(StorageTestPopulateOne, SetDefaultEntryExistsSameType) {
// existing entry
auto value = Value::MakeBoolean(true);
auto ret_val = storage.SetDefaultEntryValue("foo", value);
EXPECT_TRUE(ret_val);
EXPECT_NE(value, GetEntry("foo")->value);
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulateOne, SetDefaultEntryExistsDifferentType) {
// existing entry is boolean
auto value = Value::MakeDouble(2.0);
auto ret_val = storage.SetDefaultEntryValue("foo", value);
EXPECT_FALSE(ret_val);
// should not have updated value in table if it already existed.
EXPECT_NE(value, GetEntry("foo")->value);
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetDefaultEntryEmptyName) {
auto value = Value::MakeBoolean(true);
auto ret_val = storage.SetDefaultEntryValue("", value);
EXPECT_FALSE(ret_val);
auto entry = GetEntry("foo");
EXPECT_FALSE(entry->value);
EXPECT_EQ(0u, entry->flags);
EXPECT_EQ("foobar", entry->name); // since GetEntry uses the tmp_entry.
EXPECT_EQ(0xffffu, entry->id);
EXPECT_EQ(SequenceNumber(), entry->seq_num);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetDefaultEntryEmptyValue) {
auto value = Value::MakeBoolean(true);
auto ret_val = storage.SetDefaultEntryValue("", nullptr);
EXPECT_FALSE(ret_val);
auto entry = GetEntry("foo");
EXPECT_FALSE(entry->value);
EXPECT_EQ(0u, entry->flags);
EXPECT_EQ("foobar", entry->name); // since GetEntry uses the tmp_entry.
EXPECT_EQ(0xffffu, entry->id);
EXPECT_EQ(SequenceNumber(), entry->seq_num);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulated, SetDefaultEntryEmptyName) {
auto value = Value::MakeBoolean(true);
auto ret_val = storage.SetDefaultEntryValue("", value);
EXPECT_FALSE(ret_val);
// assert that no entries get added
EXPECT_EQ(4u, entries().size());
if (GetParam())
EXPECT_EQ(4u, idmap().size());
else
EXPECT_EQ(0u, idmap().size());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulated, SetDefaultEntryEmptyValue) {
auto value = Value::MakeBoolean(true);
auto ret_val = storage.SetDefaultEntryValue("", nullptr);
EXPECT_FALSE(ret_val);
// assert that no entries get added
EXPECT_EQ(4u, entries().size());
if (GetParam())
EXPECT_EQ(4u, idmap().size());
else
EXPECT_EQ(0u, idmap().size());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetEntryFlagsNew) {
// flags setting doesn't create an entry
storage.SetEntryFlags("foo", 0u);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulateOne, SetEntryFlagsEqualValue) {
// update with same value: no update message is issued (minimizing bandwidth
// usage)
storage.SetEntryFlags("foo", 0u);
auto entry = GetEntry("foo");
EXPECT_EQ(0u, entry->flags);
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulated, SetEntryFlagsDifferentValue) {
// update with different value results in flags update message
storage.SetEntryFlags("foo2", 1u);
EXPECT_EQ(1u, GetEntry("foo2")->flags);
if (GetParam()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kFlagsUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(1u, msg->flags());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
}
}
TEST_P(StorageTestEmpty, SetEntryFlagsEmptyName) {
storage.SetEntryFlags("", 0u);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, GetEntryFlagsNotExist) {
EXPECT_EQ(0u, storage.GetEntryFlags("foo"));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulateOne, GetEntryFlagsExist) {
storage.SetEntryFlags("foo", 1u);
outgoing.clear();
EXPECT_EQ(1u, storage.GetEntryFlags("foo"));
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, DeleteEntryNotExist) {
storage.DeleteEntry("foo");
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulated, DeleteEntryExist) {
storage.DeleteEntry("foo2");
EXPECT_TRUE(entries().count("foo2") == 0);
if (GetParam()) {
ASSERT_TRUE(idmap().size() >= 2);
EXPECT_FALSE(idmap()[1]);
}
if (GetParam()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryDelete, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
}
}
TEST_P(StorageTestEmpty, DeleteAllEntriesEmpty) {
storage.DeleteAllEntries();
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulated, DeleteAllEntries) {
storage.DeleteAllEntries();
ASSERT_TRUE(entries().empty());
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kClearEntries, msg->type());
}
TEST_P(StorageTestPopulated, DeleteAllEntriesPersistent) {
GetEntry("foo2")->flags = NT_PERSISTENT;
storage.DeleteAllEntries();
ASSERT_EQ(1u, entries().size());
EXPECT_EQ(1u, entries().count("foo2"));
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kClearEntries, msg->type());
}
TEST_P(StorageTestPopulated, GetEntryInfoAll) {
auto info = storage.GetEntryInfo("", 0u);
ASSERT_EQ(4u, info.size());
}
TEST_P(StorageTestPopulated, GetEntryInfoPrefix) {
auto info = storage.GetEntryInfo("foo", 0u);
ASSERT_EQ(2u, info.size());
if (info[0].name == "foo") {
EXPECT_EQ("foo", info[0].name);
EXPECT_EQ(NT_BOOLEAN, info[0].type);
EXPECT_EQ("foo2", info[1].name);
EXPECT_EQ(NT_DOUBLE, info[1].type);
} else {
EXPECT_EQ("foo2", info[0].name);
EXPECT_EQ(NT_DOUBLE, info[0].type);
EXPECT_EQ("foo", info[1].name);
EXPECT_EQ(NT_BOOLEAN, info[1].type);
}
}
TEST_P(StorageTestPopulated, GetEntryInfoTypes) {
auto info = storage.GetEntryInfo("", NT_DOUBLE);
ASSERT_EQ(2u, info.size());
EXPECT_EQ(NT_DOUBLE, info[0].type);
EXPECT_EQ(NT_DOUBLE, info[1].type);
if (info[0].name == "foo2") {
EXPECT_EQ("foo2", info[0].name);
EXPECT_EQ("bar", info[1].name);
} else {
EXPECT_EQ("bar", info[0].name);
EXPECT_EQ("foo2", info[1].name);
}
}
TEST_P(StorageTestPopulated, GetEntryInfoPrefixTypes) {
auto info = storage.GetEntryInfo("bar", NT_BOOLEAN);
ASSERT_EQ(1u, info.size());
EXPECT_EQ("bar2", info[0].name);
EXPECT_EQ(NT_BOOLEAN, info[0].type);
}
TEST_P(StorageTestPersistent, SavePersistentEmpty) {
std::ostringstream oss;
storage.SavePersistent(oss, false);
ASSERT_EQ("[NetworkTables Storage 3.0]\n", oss.str());
}
TEST_P(StorageTestPersistent, SavePersistent) {
for (auto& i : entries()) i.getValue()->flags = NT_PERSISTENT;
std::ostringstream oss;
storage.SavePersistent(oss, false);
std::string out = oss.str();
//fputs(out.c_str(), stderr);
llvm::StringRef line, rem = out;
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("[NetworkTables Storage 3.0]", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("boolean \"\\x00\\x03\\x05\\n\"=true", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("boolean \"\\x3D\"=true", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("boolean \"boolean/false\"=false", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("boolean \"boolean/true\"=true", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("array boolean \"booleanarr/empty\"=", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("array boolean \"booleanarr/one\"=true", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("array boolean \"booleanarr/two\"=true,false", line);
std::tie(line, rem) = rem.split('\n');
#if defined(_MSC_VER) && _MSC_VER < 1900
ASSERT_EQ("double \"double/big\"=1.3e+008", line);
#else
ASSERT_EQ("double \"double/big\"=1.3e+08", line);
#endif
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("double \"double/neg\"=-1.5", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("double \"double/zero\"=0", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("array double \"doublearr/empty\"=", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("array double \"doublearr/one\"=0.5", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("array double \"doublearr/two\"=0.5,-0.25", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("raw \"raw/empty\"=", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("raw \"raw/normal\"=aGVsbG8=", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("raw \"raw/special\"=AAMFCg==", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("string \"string/empty\"=\"\"", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("string \"string/normal\"=\"hello\"", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("string \"string/special\"=\"\\x00\\x03\\x05\\n\"", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("array string \"stringarr/empty\"=", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("array string \"stringarr/one\"=\"hello\"", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("array string \"stringarr/two\"=\"hello\",\"world\\n\"", line);
std::tie(line, rem) = rem.split('\n');
ASSERT_EQ("", line);
}
TEST_P(StorageTestEmpty, LoadPersistentBadHeader) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
std::istringstream iss("");
EXPECT_CALL(warn, Warn(1, llvm::StringRef("header line mismatch, ignoring rest of file")));
EXPECT_FALSE(storage.LoadPersistent(iss, warn_func));
std::istringstream iss2("[NetworkTables");
EXPECT_CALL(warn, Warn(1, llvm::StringRef("header line mismatch, ignoring rest of file")));
EXPECT_FALSE(storage.LoadPersistent(iss2, warn_func));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, LoadPersistentCommentHeader) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
std::istringstream iss(
"\n; comment\n# comment\n[NetworkTables Storage 3.0]\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, LoadPersistentEmptyName) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
std::istringstream iss(
"[NetworkTables Storage 3.0]\nboolean \"\"=true\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, LoadPersistentAssign) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
std::istringstream iss(
"[NetworkTables Storage 3.0]\nboolean \"foo\"=true\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
auto entry = GetEntry("foo");
EXPECT_EQ(*Value::MakeBoolean(true), *entry->value);
EXPECT_EQ(NT_PERSISTENT, entry->flags);
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryAssign, msg->type());
EXPECT_EQ("foo", msg->str());
if (GetParam())
EXPECT_EQ(0u, msg->id()); // assigned as server
else
EXPECT_EQ(0xffffu, msg->id()); // not assigned as client
EXPECT_EQ(1u, msg->seq_num_uid());
EXPECT_EQ(*Value::MakeBoolean(true), *msg->value());
EXPECT_EQ(NT_PERSISTENT, msg->flags());
}
TEST_P(StorageTestPopulated, LoadPersistentUpdateFlags) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
std::istringstream iss(
"[NetworkTables Storage 3.0]\ndouble \"foo2\"=0.0\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
auto entry = GetEntry("foo2");
EXPECT_EQ(*Value::MakeDouble(0.0), *entry->value);
EXPECT_EQ(NT_PERSISTENT, entry->flags);
if (GetParam()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kFlagsUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(NT_PERSISTENT, msg->flags());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
}
}
TEST_P(StorageTestPopulated, LoadPersistentUpdateValue) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
GetEntry("foo2")->flags = NT_PERSISTENT;
std::istringstream iss(
"[NetworkTables Storage 3.0]\ndouble \"foo2\"=1.0\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
auto entry = GetEntry("foo2");
EXPECT_EQ(*Value::MakeDouble(1.0), *entry->value);
EXPECT_EQ(NT_PERSISTENT, entry->flags);
if (GetParam()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(2u, msg->seq_num_uid()); // incremented
EXPECT_EQ(*Value::MakeDouble(1.0), *msg->value());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value()); // still should be incremented
}
}
TEST_P(StorageTestPopulated, LoadPersistentUpdateValueFlags) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
std::istringstream iss(
"[NetworkTables Storage 3.0]\ndouble \"foo2\"=1.0\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
auto entry = GetEntry("foo2");
EXPECT_EQ(*Value::MakeDouble(1.0), *entry->value);
EXPECT_EQ(NT_PERSISTENT, entry->flags);
if (GetParam()) {
ASSERT_EQ(2u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(2u, msg->seq_num_uid()); // incremented
EXPECT_EQ(*Value::MakeDouble(1.0), *msg->value());
EXPECT_FALSE(outgoing[1].only);
EXPECT_FALSE(outgoing[1].except);
msg = outgoing[1].msg;
EXPECT_EQ(Message::kFlagsUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(NT_PERSISTENT, msg->flags());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value()); // still should be incremented
}
}
TEST_P(StorageTestEmpty, LoadPersistent) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
std::string in = "[NetworkTables Storage 3.0]\n";
in += "boolean \"\\x00\\x03\\x05\\n\"=true\n";
in += "boolean \"\\x3D\"=true\n";
in += "boolean \"boolean/false\"=false\n";
in += "boolean \"boolean/true\"=true\n";
in += "array boolean \"booleanarr/empty\"=\n";
in += "array boolean \"booleanarr/one\"=true\n";
in += "array boolean \"booleanarr/two\"=true,false\n";
in += "double \"double/big\"=1.3e+08\n";
in += "double \"double/neg\"=-1.5\n";
in += "double \"double/zero\"=0\n";
in += "array double \"doublearr/empty\"=\n";
in += "array double \"doublearr/one\"=0.5\n";
in += "array double \"doublearr/two\"=0.5,-0.25\n";
in += "raw \"raw/empty\"=\n";
in += "raw \"raw/normal\"=aGVsbG8=\n";
in += "raw \"raw/special\"=AAMFCg==\n";
in += "string \"string/empty\"=\"\"\n";
in += "string \"string/normal\"=\"hello\"\n";
in += "string \"string/special\"=\"\\x00\\x03\\x05\\n\"\n";
in += "array string \"stringarr/empty\"=\n";
in += "array string \"stringarr/one\"=\"hello\"\n";
in += "array string \"stringarr/two\"=\"hello\",\"world\\n\"\n";
std::istringstream iss(in);
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
ASSERT_EQ(22u, entries().size());
EXPECT_EQ(22u, outgoing.size());
EXPECT_EQ(*Value::MakeBoolean(true), *storage.GetEntryValue("boolean/true"));
EXPECT_EQ(*Value::MakeBoolean(false),
*storage.GetEntryValue("boolean/false"));
EXPECT_EQ(*Value::MakeDouble(-1.5), *storage.GetEntryValue("double/neg"));
EXPECT_EQ(*Value::MakeDouble(0.0), *storage.GetEntryValue("double/zero"));
EXPECT_EQ(*Value::MakeDouble(1.3e8), *storage.GetEntryValue("double/big"));
EXPECT_EQ(*Value::MakeString(""), *storage.GetEntryValue("string/empty"));
EXPECT_EQ(*Value::MakeString("hello"),
*storage.GetEntryValue("string/normal"));
EXPECT_EQ(*Value::MakeString(StringRef("\0\3\5\n", 4)),
*storage.GetEntryValue("string/special"));
EXPECT_EQ(*Value::MakeRaw(""),
*storage.GetEntryValue("raw/empty"));
EXPECT_EQ(*Value::MakeRaw("hello"),
*storage.GetEntryValue("raw/normal"));
EXPECT_EQ(*Value::MakeRaw(StringRef("\0\3\5\n", 4)),
*storage.GetEntryValue("raw/special"));
EXPECT_EQ(*Value::MakeBooleanArray(std::vector<int>{}),
*storage.GetEntryValue("booleanarr/empty"));
EXPECT_EQ(*Value::MakeBooleanArray(std::vector<int>{1}),
*storage.GetEntryValue("booleanarr/one"));
EXPECT_EQ(*Value::MakeBooleanArray(std::vector<int>{1, 0}),
*storage.GetEntryValue("booleanarr/two"));
EXPECT_EQ(*Value::MakeDoubleArray(std::vector<double>{}),
*storage.GetEntryValue("doublearr/empty"));
EXPECT_EQ(*Value::MakeDoubleArray(std::vector<double>{0.5}),
*storage.GetEntryValue("doublearr/one"));
EXPECT_EQ(*Value::MakeDoubleArray(std::vector<double>{0.5, -0.25}),
*storage.GetEntryValue("doublearr/two"));
EXPECT_EQ(*Value::MakeStringArray(std::vector<std::string>{}),
*storage.GetEntryValue("stringarr/empty"));
EXPECT_EQ(*Value::MakeStringArray(std::vector<std::string>{"hello"}),
*storage.GetEntryValue("stringarr/one"));
EXPECT_EQ(
*Value::MakeStringArray(std::vector<std::string>{"hello", "world\n"}),
*storage.GetEntryValue("stringarr/two"));
EXPECT_EQ(*Value::MakeBoolean(true),
*storage.GetEntryValue(StringRef("\0\3\5\n", 4)));
EXPECT_EQ(*Value::MakeBoolean(true), *storage.GetEntryValue("="));
}
TEST_P(StorageTestEmpty, LoadPersistentWarn) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
std::istringstream iss(
"[NetworkTables Storage 3.0]\nboolean \"foo\"=foo\n");
EXPECT_CALL(warn,
Warn(2, llvm::StringRef("unrecognized boolean value, not 'true' or 'false'")));
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
INSTANTIATE_TEST_CASE_P(StorageTestsEmpty, StorageTestEmpty, ::testing::Bool());
INSTANTIATE_TEST_CASE_P(StorageTestsPopulateOne, StorageTestPopulateOne,
::testing::Bool());
INSTANTIATE_TEST_CASE_P(StorageTestsPopulated, StorageTestPopulated,
::testing::Bool());
INSTANTIATE_TEST_CASE_P(StorageTestsPersistent, StorageTestPersistent,
::testing::Bool());
} // namespace nt

View File

@@ -0,0 +1,55 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef NT_TEST_STORAGETEST_H_
#define NT_TEST_STORAGETEST_H_
#include <functional>
#include <memory>
#include <vector>
#include "Storage.h"
namespace nt {
class StorageTest {
public:
StorageTest() : tmp_entry("foobar") {}
Storage::EntriesMap& entries() { return storage.m_entries; }
Storage::IdMap& idmap() { return storage.m_idmap; }
Storage::Entry* GetEntry(StringRef name) {
auto i = storage.m_entries.find(name);
return i == storage.m_entries.end() ? &tmp_entry : i->getValue().get();
}
void HookOutgoing(bool server) {
using namespace std::placeholders;
storage.SetOutgoing(
std::bind(&StorageTest::QueueOutgoing, this, _1, _2, _3), server);
}
struct OutgoingData {
std::shared_ptr<Message> msg;
NetworkConnection* only;
NetworkConnection* except;
};
void QueueOutgoing(std::shared_ptr<Message> msg, NetworkConnection* only,
NetworkConnection* except) {
outgoing.emplace_back(OutgoingData{msg, only, except});
}
Storage storage;
Storage::Entry tmp_entry;
std::vector<OutgoingData> outgoing;
};
} // namespace nt
#endif // NT_TEST_STORAGETEST_H_

View File

@@ -0,0 +1,365 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "nt_Value.h"
#include "Value_internal.h"
#include "gtest/gtest.h"
namespace nt {
class ValueTest : public ::testing::Test {};
typedef ValueTest ValueDeathTest;
TEST_F(ValueTest, ConstructEmpty) {
Value v;
ASSERT_EQ(NT_UNASSIGNED, v.type());
}
TEST_F(ValueTest, Boolean) {
auto v = Value::MakeBoolean(false);
ASSERT_EQ(NT_BOOLEAN, v->type());
ASSERT_FALSE(v->GetBoolean());
NT_Value cv;
NT_InitValue(&cv);
ConvertToC(*v, &cv);
ASSERT_EQ(NT_BOOLEAN, cv.type);
ASSERT_EQ(0, cv.data.v_boolean);
v = Value::MakeBoolean(true);
ASSERT_EQ(NT_BOOLEAN, v->type());
ASSERT_TRUE(v->GetBoolean());
ConvertToC(*v, &cv);
ASSERT_EQ(NT_BOOLEAN, cv.type);
ASSERT_EQ(1, cv.data.v_boolean);
NT_DisposeValue(&cv);
}
TEST_F(ValueTest, Double) {
auto v = Value::MakeDouble(0.5);
ASSERT_EQ(NT_DOUBLE, v->type());
ASSERT_EQ(0.5, v->GetDouble());
NT_Value cv;
NT_InitValue(&cv);
ConvertToC(*v, &cv);
ASSERT_EQ(NT_DOUBLE, cv.type);
ASSERT_EQ(0.5, cv.data.v_double);
v = Value::MakeDouble(0.25);
ASSERT_EQ(NT_DOUBLE, v->type());
ASSERT_EQ(0.25, v->GetDouble());
ConvertToC(*v, &cv);
ASSERT_EQ(NT_DOUBLE, cv.type);
ASSERT_EQ(0.25, cv.data.v_double);
NT_DisposeValue(&cv);
}
TEST_F(ValueTest, String) {
auto v = Value::MakeString("hello");
ASSERT_EQ(NT_STRING, v->type());
ASSERT_EQ("hello", v->GetString());
NT_Value cv;
NT_InitValue(&cv);
ConvertToC(*v, &cv);
ASSERT_EQ(NT_STRING, cv.type);
ASSERT_EQ(llvm::StringRef("hello"), cv.data.v_string.str);
ASSERT_EQ(5u, cv.data.v_string.len);
v = Value::MakeString("goodbye");
ASSERT_EQ(NT_STRING, v->type());
ASSERT_EQ("goodbye", v->GetString());
ConvertToC(*v, &cv);
ASSERT_EQ(NT_STRING, cv.type);
ASSERT_EQ(llvm::StringRef("goodbye"), cv.data.v_string.str);
ASSERT_EQ(7u, cv.data.v_string.len);
NT_DisposeValue(&cv);
}
TEST_F(ValueTest, Raw) {
auto v = Value::MakeRaw("hello");
ASSERT_EQ(NT_RAW, v->type());
ASSERT_EQ("hello", v->GetRaw());
NT_Value cv;
NT_InitValue(&cv);
ConvertToC(*v, &cv);
ASSERT_EQ(NT_RAW, cv.type);
ASSERT_EQ(llvm::StringRef("hello"), cv.data.v_string.str);
ASSERT_EQ(5u, cv.data.v_string.len);
v = Value::MakeRaw("goodbye");
ASSERT_EQ(NT_RAW, v->type());
ASSERT_EQ("goodbye", v->GetRaw());
ConvertToC(*v, &cv);
ASSERT_EQ(NT_RAW, cv.type);
ASSERT_EQ(llvm::StringRef("goodbye"), cv.data.v_string.str);
ASSERT_EQ(7u, cv.data.v_string.len);
NT_DisposeValue(&cv);
}
TEST_F(ValueTest, BooleanArray) {
std::vector<int> vec{1,0,1};
auto v = Value::MakeBooleanArray(vec);
ASSERT_EQ(NT_BOOLEAN_ARRAY, v->type());
ASSERT_EQ(llvm::ArrayRef<int>(vec), v->GetBooleanArray());
NT_Value cv;
NT_InitValue(&cv);
ConvertToC(*v, &cv);
ASSERT_EQ(NT_BOOLEAN_ARRAY, cv.type);
ASSERT_EQ(3u, cv.data.arr_boolean.size);
ASSERT_EQ(vec[0], cv.data.arr_boolean.arr[0]);
ASSERT_EQ(vec[1], cv.data.arr_boolean.arr[1]);
ASSERT_EQ(vec[2], cv.data.arr_boolean.arr[2]);
// assign with same size
vec = {0,1,0};
v = Value::MakeBooleanArray(vec);
ASSERT_EQ(NT_BOOLEAN_ARRAY, v->type());
ASSERT_EQ(llvm::ArrayRef<int>(vec), v->GetBooleanArray());
ConvertToC(*v, &cv);
ASSERT_EQ(NT_BOOLEAN_ARRAY, cv.type);
ASSERT_EQ(3u, cv.data.arr_boolean.size);
ASSERT_EQ(vec[0], cv.data.arr_boolean.arr[0]);
ASSERT_EQ(vec[1], cv.data.arr_boolean.arr[1]);
ASSERT_EQ(vec[2], cv.data.arr_boolean.arr[2]);
// assign with different size
vec = {1,0};
v = Value::MakeBooleanArray(vec);
ASSERT_EQ(NT_BOOLEAN_ARRAY, v->type());
ASSERT_EQ(llvm::ArrayRef<int>(vec), v->GetBooleanArray());
ConvertToC(*v, &cv);
ASSERT_EQ(NT_BOOLEAN_ARRAY, cv.type);
ASSERT_EQ(2u, cv.data.arr_boolean.size);
ASSERT_EQ(vec[0], cv.data.arr_boolean.arr[0]);
ASSERT_EQ(vec[1], cv.data.arr_boolean.arr[1]);
NT_DisposeValue(&cv);
}
TEST_F(ValueTest, DoubleArray) {
std::vector<double> vec{0.5,0.25,0.5};
auto v = Value::MakeDoubleArray(vec);
ASSERT_EQ(NT_DOUBLE_ARRAY, v->type());
ASSERT_EQ(llvm::ArrayRef<double>(vec), v->GetDoubleArray());
NT_Value cv;
NT_InitValue(&cv);
ConvertToC(*v, &cv);
ASSERT_EQ(NT_DOUBLE_ARRAY, cv.type);
ASSERT_EQ(3u, cv.data.arr_double.size);
ASSERT_EQ(vec[0], cv.data.arr_double.arr[0]);
ASSERT_EQ(vec[1], cv.data.arr_double.arr[1]);
ASSERT_EQ(vec[2], cv.data.arr_double.arr[2]);
// assign with same size
vec = {0.25,0.5,0.25};
v = Value::MakeDoubleArray(vec);
ASSERT_EQ(NT_DOUBLE_ARRAY, v->type());
ASSERT_EQ(llvm::ArrayRef<double>(vec), v->GetDoubleArray());
ConvertToC(*v, &cv);
ASSERT_EQ(NT_DOUBLE_ARRAY, cv.type);
ASSERT_EQ(3u, cv.data.arr_double.size);
ASSERT_EQ(vec[0], cv.data.arr_double.arr[0]);
ASSERT_EQ(vec[1], cv.data.arr_double.arr[1]);
ASSERT_EQ(vec[2], cv.data.arr_double.arr[2]);
// assign with different size
vec = {0.5,0.25};
v = Value::MakeDoubleArray(vec);
ASSERT_EQ(NT_DOUBLE_ARRAY, v->type());
ASSERT_EQ(llvm::ArrayRef<double>(vec), v->GetDoubleArray());
ConvertToC(*v, &cv);
ASSERT_EQ(NT_DOUBLE_ARRAY, cv.type);
ASSERT_EQ(2u, cv.data.arr_double.size);
ASSERT_EQ(vec[0], cv.data.arr_double.arr[0]);
ASSERT_EQ(vec[1], cv.data.arr_double.arr[1]);
NT_DisposeValue(&cv);
}
TEST_F(ValueTest, StringArray) {
std::vector<std::string> vec;
vec.push_back("hello");
vec.push_back("goodbye");
vec.push_back("string");
auto v = Value::MakeStringArray(std::move(vec));
ASSERT_EQ(NT_STRING_ARRAY, v->type());
ASSERT_EQ(3u, v->GetStringArray().size());
ASSERT_EQ(llvm::StringRef("hello"), v->GetStringArray()[0]);
ASSERT_EQ(llvm::StringRef("goodbye"), v->GetStringArray()[1]);
ASSERT_EQ(llvm::StringRef("string"), v->GetStringArray()[2]);
NT_Value cv;
NT_InitValue(&cv);
ConvertToC(*v, &cv);
ASSERT_EQ(NT_STRING_ARRAY, cv.type);
ASSERT_EQ(3u, cv.data.arr_string.size);
ASSERT_EQ(llvm::StringRef("hello"), cv.data.arr_string.arr[0].str);
ASSERT_EQ(llvm::StringRef("goodbye"), cv.data.arr_string.arr[1].str);
ASSERT_EQ(llvm::StringRef("string"), cv.data.arr_string.arr[2].str);
// assign with same size
vec.clear();
vec.push_back("s1");
vec.push_back("str2");
vec.push_back("string3");
v = Value::MakeStringArray(vec);
ASSERT_EQ(NT_STRING_ARRAY, v->type());
ASSERT_EQ(3u, v->GetStringArray().size());
ASSERT_EQ(llvm::StringRef("s1"), v->GetStringArray()[0]);
ASSERT_EQ(llvm::StringRef("str2"), v->GetStringArray()[1]);
ASSERT_EQ(llvm::StringRef("string3"), v->GetStringArray()[2]);
ConvertToC(*v, &cv);
ASSERT_EQ(NT_STRING_ARRAY, cv.type);
ASSERT_EQ(3u, cv.data.arr_string.size);
ASSERT_EQ(llvm::StringRef("s1"), cv.data.arr_string.arr[0].str);
ASSERT_EQ(llvm::StringRef("str2"), cv.data.arr_string.arr[1].str);
ASSERT_EQ(llvm::StringRef("string3"), cv.data.arr_string.arr[2].str);
// assign with different size
vec.clear();
vec.push_back("short");
vec.push_back("er");
v = Value::MakeStringArray(std::move(vec));
ASSERT_EQ(NT_STRING_ARRAY, v->type());
ASSERT_EQ(2u, v->GetStringArray().size());
ASSERT_EQ(llvm::StringRef("short"), v->GetStringArray()[0]);
ASSERT_EQ(llvm::StringRef("er"), v->GetStringArray()[1]);
ConvertToC(*v, &cv);
ASSERT_EQ(NT_STRING_ARRAY, cv.type);
ASSERT_EQ(2u, cv.data.arr_string.size);
ASSERT_EQ(llvm::StringRef("short"), cv.data.arr_string.arr[0].str);
ASSERT_EQ(llvm::StringRef("er"), cv.data.arr_string.arr[1].str);
NT_DisposeValue(&cv);
}
TEST_F(ValueDeathTest, GetAssertions) {
Value v;
ASSERT_DEATH(v.GetBoolean(), "type == NT_BOOLEAN");
ASSERT_DEATH(v.GetDouble(), "type == NT_DOUBLE");
ASSERT_DEATH(v.GetString(), "type == NT_STRING");
ASSERT_DEATH(v.GetRaw(), "type == NT_RAW");
ASSERT_DEATH(v.GetBooleanArray(), "type == NT_BOOLEAN_ARRAY");
ASSERT_DEATH(v.GetDoubleArray(), "type == NT_DOUBLE_ARRAY");
ASSERT_DEATH(v.GetStringArray(), "type == NT_STRING_ARRAY");
}
TEST_F(ValueTest, UnassignedComparison) {
Value v1, v2;
ASSERT_EQ(v1, v2);
}
TEST_F(ValueTest, MixedComparison) {
Value v1;
auto v2 = Value::MakeBoolean(true);
ASSERT_NE(v1, *v2); // unassigned vs boolean
auto v3 = Value::MakeDouble(0.5);
ASSERT_NE(*v2, *v3); // boolean vs double
}
TEST_F(ValueTest, BooleanComparison) {
auto v1 = Value::MakeBoolean(true);
auto v2 = Value::MakeBoolean(true);
ASSERT_EQ(*v1, *v2);
v2 = Value::MakeBoolean(false);
ASSERT_NE(*v1, *v2);
}
TEST_F(ValueTest, DoubleComparison) {
auto v1 = Value::MakeDouble(0.25);
auto v2 = Value::MakeDouble(0.25);
ASSERT_EQ(*v1, *v2);
v2 = Value::MakeDouble(0.5);
ASSERT_NE(*v1, *v2);
}
TEST_F(ValueTest, StringComparison) {
auto v1 = Value::MakeString("hello");
auto v2 = Value::MakeString("hello");
ASSERT_EQ(*v1, *v2);
v2 = Value::MakeString("world"); // different contents
ASSERT_NE(*v1, *v2);
v2 = Value::MakeString("goodbye"); // different size
ASSERT_NE(*v1, *v2);
}
TEST_F(ValueTest, BooleanArrayComparison) {
std::vector<int> vec{1,0,1};
auto v1 = Value::MakeBooleanArray(vec);
auto v2 = Value::MakeBooleanArray(vec);
ASSERT_EQ(*v1, *v2);
// different contents
vec = {1,1,1};
v2 = Value::MakeBooleanArray(vec);
ASSERT_NE(*v1, *v2);
// different size
vec = {1,0};
v2 = Value::MakeBooleanArray(vec);
ASSERT_NE(*v1, *v2);
}
TEST_F(ValueTest, DoubleArrayComparison) {
std::vector<double> vec{0.5,0.25,0.5};
auto v1 = Value::MakeDoubleArray(vec);
auto v2 = Value::MakeDoubleArray(vec);
ASSERT_EQ(*v1, *v2);
// different contents
vec = {0.5,0.5,0.5};
v2 = Value::MakeDoubleArray(vec);
ASSERT_NE(*v1, *v2);
// different size
vec = {0.5,0.25};
v2 = Value::MakeDoubleArray(vec);
ASSERT_NE(*v1, *v2);
}
TEST_F(ValueTest, StringArrayComparison) {
std::vector<std::string> vec;
vec.push_back("hello");
vec.push_back("goodbye");
vec.push_back("string");
auto v1 = Value::MakeStringArray(vec);
vec.clear();
vec.push_back("hello");
vec.push_back("goodbye");
vec.push_back("string");
auto v2 = Value::MakeStringArray(std::move(vec));
ASSERT_EQ(*v1, *v2);
// different contents
vec.clear();
vec.push_back("hello");
vec.push_back("goodby2");
vec.push_back("string");
v2 = Value::MakeStringArray(std::move(vec));
ASSERT_NE(*v1, *v2);
// different sized contents
vec.clear();
vec.push_back("hello");
vec.push_back("goodbye2");
vec.push_back("string");
v2 = Value::MakeStringArray(vec);
ASSERT_NE(*v1, *v2);
// different size
vec.clear();
vec.push_back("hello");
vec.push_back("goodbye");
v2 = Value::MakeStringArray(std::move(vec));
ASSERT_NE(*v1, *v2);
}
} // namespace nt

View File

@@ -0,0 +1,602 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "WireDecoder.h"
#include "gtest/gtest.h"
#include <cfloat>
#include <climits>
#include <string>
#include "llvm/StringRef.h"
namespace nt {
class WireDecoderTest : public ::testing::Test {
protected:
WireDecoderTest() {
v_boolean = Value::MakeBoolean(true);
v_double = Value::MakeDouble(1.0);
v_string = Value::MakeString(llvm::StringRef("hello"));
v_raw = Value::MakeRaw(llvm::StringRef("hello"));
v_boolean_array = Value::MakeBooleanArray(std::vector<int>{0, 1, 0});
v_boolean_array_big = Value::MakeBooleanArray(std::vector<int>(255));
v_double_array = Value::MakeDoubleArray(std::vector<double>{0.5, 0.25});
v_double_array_big = Value::MakeDoubleArray(std::vector<double>(255));
std::vector<std::string> sa;
sa.push_back("hello");
sa.push_back("goodbye");
v_string_array = Value::MakeStringArray(std::move(sa));
sa.clear();
for (int i=0; i<255; ++i)
sa.push_back("h");
v_string_array_big = Value::MakeStringArray(std::move(sa));
s_normal = std::string("hello");
s_long.clear();
s_long.append(127, '*');
s_long.push_back('x');
s_big2.clear();
s_big2.append(65534, '*');
s_big2.push_back('x');
s_big3.clear();
s_big3.append(65534, '*');
s_big3.append(3, 'x');
}
std::shared_ptr<Value> v_boolean, v_double, v_string, v_raw;
std::shared_ptr<Value> v_boolean_array, v_boolean_array_big;
std::shared_ptr<Value> v_double_array, v_double_array_big;
std::shared_ptr<Value> v_string_array, v_string_array_big;
std::string s_normal, s_long, s_big2, s_big3;
};
TEST_F(WireDecoderTest, Construct) {
wpi::raw_mem_istream is("", 0);
WireDecoder d(is, 0x0300u);
EXPECT_EQ(nullptr, d.error());
EXPECT_EQ(0x0300u, d.proto_rev());
}
TEST_F(WireDecoderTest, SetProtoRev) {
wpi::raw_mem_istream is("", 0);
WireDecoder d(is, 0x0300u);
d.set_proto_rev(0x0200u);
EXPECT_EQ(0x0200u, d.proto_rev());
}
TEST_F(WireDecoderTest, Read8) {
wpi::raw_mem_istream is("\x05\x01\x00", 3);
WireDecoder d(is, 0x0300u);
unsigned int val;
ASSERT_TRUE(d.Read8(&val));
EXPECT_EQ(5u, val);
ASSERT_TRUE(d.Read8(&val));
EXPECT_EQ(1u, val);
ASSERT_TRUE(d.Read8(&val));
EXPECT_EQ(0u, val);
ASSERT_FALSE(d.Read8(&val));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, Read16) {
wpi::raw_mem_istream is("\x00\x05\x00\x01\x45\x67\x00\x00", 8);
WireDecoder d(is, 0x0300u);
unsigned int val;
ASSERT_TRUE(d.Read16(&val));
EXPECT_EQ(5u, val);
ASSERT_TRUE(d.Read16(&val));
EXPECT_EQ(1u, val);
ASSERT_TRUE(d.Read16(&val));
EXPECT_EQ(0x4567u, val);
ASSERT_TRUE(d.Read16(&val));
EXPECT_EQ(0u, val);
ASSERT_FALSE(d.Read16(&val));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, Read32) {
wpi::raw_mem_istream is(
"\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\xab\xcd"
"\x12\x34\x56\x78\x00\x00\x00\x00",
20);
WireDecoder d(is, 0x0300u);
unsigned long val;
ASSERT_TRUE(d.Read32(&val));
EXPECT_EQ(5ul, val);
ASSERT_TRUE(d.Read32(&val));
EXPECT_EQ(1ul, val);
ASSERT_TRUE(d.Read32(&val));
EXPECT_EQ(0xabcdul, val);
ASSERT_TRUE(d.Read32(&val));
EXPECT_EQ(0x12345678ul, val);
ASSERT_TRUE(d.Read32(&val));
EXPECT_EQ(0ul, val);
ASSERT_FALSE(d.Read32(&val));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadDouble) {
// values except min and max from
// http://www.binaryconvert.com/result_double.html
wpi::raw_mem_istream is(
"\x00\x00\x00\x00\x00\x00\x00\x00"
"\x41\x0c\x13\x80\x00\x00\x00\x00"
"\x7f\xf0\x00\x00\x00\x00\x00\x00"
"\x00\x10\x00\x00\x00\x00\x00\x00"
"\x7f\xef\xff\xff\xff\xff\xff\xff",
40);
WireDecoder d(is, 0x0300u);
double val;
ASSERT_TRUE(d.ReadDouble(&val));
EXPECT_EQ(0.0, val);
ASSERT_TRUE(d.ReadDouble(&val));
EXPECT_EQ(2.3e5, val);
ASSERT_TRUE(d.ReadDouble(&val));
EXPECT_EQ(std::numeric_limits<double>::infinity(), val);
ASSERT_TRUE(d.ReadDouble(&val));
EXPECT_EQ(DBL_MIN, val);
ASSERT_TRUE(d.ReadDouble(&val));
EXPECT_EQ(DBL_MAX, val);
ASSERT_FALSE(d.ReadDouble(&val));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadUleb128) {
wpi::raw_mem_istream is("\x00\x7f\x80\x01\x80", 5);
WireDecoder d(is, 0x0300u);
unsigned long val;
ASSERT_TRUE(d.ReadUleb128(&val));
EXPECT_EQ(0ul, val);
ASSERT_TRUE(d.ReadUleb128(&val));
EXPECT_EQ(0x7ful, val);
ASSERT_TRUE(d.ReadUleb128(&val));
EXPECT_EQ(0x80ul, val);
ASSERT_FALSE(d.ReadUleb128(&val)); // partial
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadType) {
wpi::raw_mem_istream is("\x00\x01\x02\x03\x10\x11\x12\x20", 8);
WireDecoder d(is, 0x0300u);
NT_Type val;
ASSERT_TRUE(d.ReadType(&val));
EXPECT_EQ(NT_BOOLEAN, val);
ASSERT_TRUE(d.ReadType(&val));
EXPECT_EQ(NT_DOUBLE, val);
ASSERT_TRUE(d.ReadType(&val));
EXPECT_EQ(NT_STRING, val);
ASSERT_TRUE(d.ReadType(&val));
EXPECT_EQ(NT_RAW, val);
ASSERT_TRUE(d.ReadType(&val));
EXPECT_EQ(NT_BOOLEAN_ARRAY, val);
ASSERT_TRUE(d.ReadType(&val));
EXPECT_EQ(NT_DOUBLE_ARRAY, val);
ASSERT_TRUE(d.ReadType(&val));
EXPECT_EQ(NT_STRING_ARRAY, val);
ASSERT_TRUE(d.ReadType(&val));
EXPECT_EQ(NT_RPC, val);
ASSERT_FALSE(d.ReadType(&val));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadTypeError) {
wpi::raw_mem_istream is("\x30", 1);
WireDecoder d(is, 0x0200u);
NT_Type val;
ASSERT_FALSE(d.ReadType(&val));
EXPECT_EQ(NT_UNASSIGNED, val);
ASSERT_NE(nullptr, d.error());
}
TEST_F(WireDecoderTest, Reset) {
wpi::raw_mem_istream is("\x30", 1);
WireDecoder d(is, 0x0200u);
NT_Type val;
ASSERT_FALSE(d.ReadType(&val));
EXPECT_EQ(NT_UNASSIGNED, val);
ASSERT_NE(nullptr, d.error());
d.Reset();
EXPECT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadBooleanValue2) {
wpi::raw_mem_istream is("\x01\x00", 2);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_BOOLEAN);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean, *val);
auto v_false = Value::MakeBoolean(false);
val = d.ReadValue(NT_BOOLEAN);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_false, *val);
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadDoubleValue2) {
wpi::raw_mem_istream is(
"\x3f\xf0\x00\x00\x00\x00\x00\x00"
"\x3f\xf0\x00\x00\x00\x00\x00\x00",
16);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_DOUBLE);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double, *val);
val = d.ReadValue(NT_DOUBLE);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double, *val);
ASSERT_FALSE(d.ReadValue(NT_DOUBLE));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadStringValue2) {
wpi::raw_mem_istream is("\x00\x05hello\x00\x03" "bye\x55", 13);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_STRING);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string, *val);
auto v_bye = Value::MakeString(llvm::StringRef("bye"));
val = d.ReadValue(NT_STRING);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_bye, *val);
unsigned int b;
ASSERT_TRUE(d.Read8(&b));
EXPECT_EQ(0x55u, b);
ASSERT_FALSE(d.ReadValue(NT_STRING));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadBooleanArrayValue2) {
wpi::raw_mem_istream is("\x03\x00\x01\x00\x02\x01\x00\xff", 8);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean_array, *val);
auto v_boolean_array2 = Value::MakeBooleanArray(std::vector<int>{1, 0});
val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean_array2, *val);
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadBooleanArrayBigValue2) {
std::string s;
s.push_back('\xff');
s.append(255, '\x00');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean_array_big, *val);
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadDoubleArrayValue2) {
wpi::raw_mem_istream is(
"\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
"\x3f\xd0\x00\x00\x00\x00\x00\x00\x55",
18);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double_array, *val);
unsigned int b;
ASSERT_TRUE(d.Read8(&b));
EXPECT_EQ(0x55u, b);
ASSERT_FALSE(d.ReadValue(NT_DOUBLE_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadDoubleArrayBigValue2) {
std::string s;
s.push_back('\xff');
s.append(255*8, '\x00');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double_array_big, *val);
ASSERT_FALSE(d.ReadValue(NT_DOUBLE_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadStringArrayValue2) {
wpi::raw_mem_istream is("\x02\x00\x05hello\x00\x07goodbye\x55", 18);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string_array, *val);
unsigned int b;
ASSERT_TRUE(d.Read8(&b));
EXPECT_EQ(0x55u, b);
ASSERT_FALSE(d.ReadValue(NT_STRING_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadStringArrayBigValue2) {
std::string s;
s.push_back('\xff');
for (int i=0; i<255; ++i)
s.append("\x00\x01h", 3);
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string_array_big, *val);
ASSERT_FALSE(d.ReadValue(NT_STRING_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadValueError2) {
wpi::raw_mem_istream is("", 0);
WireDecoder d(is, 0x0200u);
ASSERT_FALSE(d.ReadValue(NT_UNASSIGNED)); // unassigned
ASSERT_NE(nullptr, d.error());
d.Reset();
ASSERT_FALSE(d.ReadValue(NT_RAW)); // not supported
ASSERT_NE(nullptr, d.error());
d.Reset();
ASSERT_FALSE(d.ReadValue(NT_RPC)); // not supported
ASSERT_NE(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadBooleanValue3) {
wpi::raw_mem_istream is("\x01\x00", 2);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_BOOLEAN);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean, *val);
auto v_false = Value::MakeBoolean(false);
val = d.ReadValue(NT_BOOLEAN);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_false, *val);
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadDoubleValue3) {
wpi::raw_mem_istream is(
"\x3f\xf0\x00\x00\x00\x00\x00\x00"
"\x3f\xf0\x00\x00\x00\x00\x00\x00",
16);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_DOUBLE);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double, *val);
val = d.ReadValue(NT_DOUBLE);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double, *val);
ASSERT_FALSE(d.ReadValue(NT_DOUBLE));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadStringValue3) {
wpi::raw_mem_istream is("\x05hello\x03" "bye\x55", 11);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_STRING);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string, *val);
auto v_bye = Value::MakeString(llvm::StringRef("bye"));
val = d.ReadValue(NT_STRING);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_bye, *val);
unsigned int b;
ASSERT_TRUE(d.Read8(&b));
EXPECT_EQ(0x55u, b);
ASSERT_FALSE(d.ReadValue(NT_STRING));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadRawValue3) {
wpi::raw_mem_istream is("\x05hello\x03" "bye\x55", 11);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_RAW);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_raw, *val);
auto v_bye = Value::MakeRaw(llvm::StringRef("bye"));
val = d.ReadValue(NT_RAW);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_bye, *val);
unsigned int b;
ASSERT_TRUE(d.Read8(&b));
EXPECT_EQ(0x55u, b);
ASSERT_FALSE(d.ReadValue(NT_RAW));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadBooleanArrayValue3) {
wpi::raw_mem_istream is("\x03\x00\x01\x00\x02\x01\x00\xff", 8);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean_array, *val);
auto v_boolean_array2 = Value::MakeBooleanArray(std::vector<int>{1, 0});
val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean_array2, *val);
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadBooleanArrayBigValue3) {
std::string s;
s.push_back('\xff');
s.append(255, '\x00');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean_array_big, *val);
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadDoubleArrayValue3) {
wpi::raw_mem_istream is(
"\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
"\x3f\xd0\x00\x00\x00\x00\x00\x00\x55",
18);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double_array, *val);
unsigned int b;
ASSERT_TRUE(d.Read8(&b));
EXPECT_EQ(0x55u, b);
ASSERT_FALSE(d.ReadValue(NT_DOUBLE_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadDoubleArrayBigValue3) {
std::string s;
s.push_back('\xff');
s.append(255*8, '\x00');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double_array_big, *val);
ASSERT_FALSE(d.ReadValue(NT_DOUBLE_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadStringArrayValue3) {
wpi::raw_mem_istream is("\x02\x05hello\x07goodbye\x55", 16);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string_array, *val);
unsigned int b;
ASSERT_TRUE(d.Read8(&b));
EXPECT_EQ(0x55u, b);
ASSERT_FALSE(d.ReadValue(NT_STRING_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadStringArrayBigValue3) {
std::string s;
s.push_back('\xff');
for (int i=0; i<255; ++i)
s.append("\x01h", 2);
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string_array_big, *val);
ASSERT_FALSE(d.ReadValue(NT_STRING_ARRAY));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadValueError3) {
wpi::raw_mem_istream is("", 0);
WireDecoder d(is, 0x0200u);
ASSERT_FALSE(d.ReadValue(NT_UNASSIGNED)); // unassigned
ASSERT_NE(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadString2) {
std::string s;
s.append("\x00\x05", 2);
s += s_normal;
s.append("\x00\x80", 2);
s += s_long;
s.append("\xff\xff", 2);
s += s_big2;
s.push_back('\x55');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0200u);
std::string outs;
ASSERT_TRUE(d.ReadString(&outs));
EXPECT_EQ(s_normal, outs);
ASSERT_TRUE(d.ReadString(&outs));
EXPECT_EQ(s_long, outs);
ASSERT_TRUE(d.ReadString(&outs));
EXPECT_EQ(s_big2, outs);
unsigned int b;
ASSERT_TRUE(d.Read8(&b));
EXPECT_EQ(0x55u, b);
ASSERT_FALSE(d.ReadString(&outs));
ASSERT_EQ(nullptr, d.error());
}
TEST_F(WireDecoderTest, ReadString3) {
std::string s;
s.push_back('\x05');
s += s_normal;
s.append("\x80\x01", 2);
s += s_long;
s.append("\x81\x80\x04", 3);
s += s_big3;
s.push_back('\x55');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0300u);
std::string outs;
ASSERT_TRUE(d.ReadString(&outs));
EXPECT_EQ(s_normal, outs);
ASSERT_TRUE(d.ReadString(&outs));
EXPECT_EQ(s_long, outs);
ASSERT_TRUE(d.ReadString(&outs));
EXPECT_EQ(s_big3, outs);
unsigned int b;
ASSERT_TRUE(d.Read8(&b));
EXPECT_EQ(0x55u, b);
ASSERT_FALSE(d.ReadString(&outs));
ASSERT_EQ(nullptr, d.error());
}
} // namespace nt

View File

@@ -0,0 +1,499 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "WireEncoder.h"
#include "gtest/gtest.h"
#include <cfloat>
#include <climits>
#include <string>
#include "llvm/StringRef.h"
#define BUFSIZE 1024
namespace nt {
class WireEncoderTest : public ::testing::Test {
protected:
WireEncoderTest() {
v_empty = std::make_shared<Value>();
v_boolean = Value::MakeBoolean(true);
v_double = Value::MakeDouble(1.0);
v_string = Value::MakeString(llvm::StringRef("hello"));
v_raw = Value::MakeRaw(llvm::StringRef("hello"));
v_boolean_array = Value::MakeBooleanArray(std::vector<int>{0, 1, 0});
v_boolean_array_big = Value::MakeBooleanArray(std::vector<int>(256));
v_double_array = Value::MakeDoubleArray(std::vector<double>{0.5, 0.25});
v_double_array_big = Value::MakeDoubleArray(std::vector<double>(256));
std::vector<std::string> sa;
sa.push_back("hello");
sa.push_back("goodbye");
v_string_array = Value::MakeStringArray(std::move(sa));
sa.clear();
for (int i=0; i<256; ++i)
sa.push_back("h");
v_string_array_big = Value::MakeStringArray(std::move(sa));
s_normal = "hello";
s_long.clear();
s_long.append(127, '*');
s_long.push_back('x');
s_big.clear();
s_big.append(65534, '*');
s_big.append(3, 'x');
}
std::shared_ptr<Value> v_empty;
std::shared_ptr<Value> v_boolean, v_double, v_string, v_raw;
std::shared_ptr<Value> v_boolean_array, v_boolean_array_big;
std::shared_ptr<Value> v_double_array, v_double_array_big;
std::shared_ptr<Value> v_string_array, v_string_array_big;
std::string s_normal, s_long, s_big;
};
TEST_F(WireEncoderTest, Construct) {
WireEncoder e(0x0300u);
EXPECT_EQ(0u, e.size());
EXPECT_EQ(nullptr, e.error());
EXPECT_EQ(0x0300u, e.proto_rev());
}
TEST_F(WireEncoderTest, SetProtoRev) {
WireEncoder e(0x0300u);
e.set_proto_rev(0x0200u);
EXPECT_EQ(0x0200u, e.proto_rev());
}
TEST_F(WireEncoderTest, Write8) {
std::size_t off = BUFSIZE-1;
WireEncoder e(0x0300u);
for(std::size_t i=0; i<off; ++i) e.Write8(0u); // test across Reserve()
e.Write8(5u);
e.Write8(0x101u); // should be truncated
e.Write8(0u);
ASSERT_EQ(3u, e.size()-off);
ASSERT_EQ(llvm::StringRef("\x05\x01\x00", 3),
llvm::StringRef(e.data(), e.size()).substr(off));
}
TEST_F(WireEncoderTest, Write16) {
std::size_t off = BUFSIZE-2;
WireEncoder e(0x0300u);
for(std::size_t i=0; i<off; ++i) e.Write8(0u); // test across Reserve()
e.Write16(5u);
e.Write16(0x10001u); // should be truncated
e.Write16(0x4567u);
e.Write16(0u);
ASSERT_EQ(8u, e.size()-off);
ASSERT_EQ(llvm::StringRef("\x00\x05\x00\x01\x45\x67\x00\x00", 8),
llvm::StringRef(e.data(), e.size()).substr(off));
}
TEST_F(WireEncoderTest, Write32) {
std::size_t off = BUFSIZE-4;
WireEncoder e(0x0300u);
for(std::size_t i=0; i<off; ++i) e.Write8(0u); // test across Reserve()
e.Write32(5ul);
e.Write32(1ul);
e.Write32(0xabcdul);
e.Write32(0x12345678ul);
e.Write32(0ul);
ASSERT_EQ(20u, e.size()-off);
ASSERT_EQ(llvm::StringRef(
"\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\xab\xcd"
"\x12\x34\x56\x78\x00\x00\x00\x00",
20),
llvm::StringRef(e.data(), e.size()).substr(off));
}
TEST_F(WireEncoderTest, WriteDouble) {
std::size_t off = BUFSIZE-8;
WireEncoder e(0x0300u);
for(std::size_t i=0; i<off; ++i) e.Write8(0u); // test across Reserve()
e.WriteDouble(0.0);
e.WriteDouble(2.3e5);
e.WriteDouble(std::numeric_limits<double>::infinity());
e.WriteDouble(DBL_MIN);
e.WriteDouble(DBL_MAX);
ASSERT_EQ(40u, e.size()-off);
// golden values except min and max from
// http://www.binaryconvert.com/result_double.html
ASSERT_EQ(llvm::StringRef(
"\x00\x00\x00\x00\x00\x00\x00\x00"
"\x41\x0c\x13\x80\x00\x00\x00\x00"
"\x7f\xf0\x00\x00\x00\x00\x00\x00"
"\x00\x10\x00\x00\x00\x00\x00\x00"
"\x7f\xef\xff\xff\xff\xff\xff\xff",
40),
llvm::StringRef(e.data(), e.size()).substr(off));
}
TEST_F(WireEncoderTest, WriteUleb128) {
std::size_t off = BUFSIZE-2;
WireEncoder e(0x0300u);
for(std::size_t i=0; i<off; ++i) e.Write8(0u); // test across Reserve()
e.WriteUleb128(0ul);
e.WriteUleb128(0x7ful);
e.WriteUleb128(0x80ul);
ASSERT_EQ(4u, e.size()-off);
ASSERT_EQ(llvm::StringRef("\x00\x7f\x80\x01", 4),
llvm::StringRef(e.data(), e.size()).substr(off));
}
TEST_F(WireEncoderTest, WriteType) {
std::size_t off = BUFSIZE-1;
WireEncoder e(0x0300u);
for(std::size_t i=0; i<off; ++i) e.Write8(0u); // test across Reserve()
e.WriteType(NT_BOOLEAN);
e.WriteType(NT_DOUBLE);
e.WriteType(NT_STRING);
e.WriteType(NT_RAW);
e.WriteType(NT_BOOLEAN_ARRAY);
e.WriteType(NT_DOUBLE_ARRAY);
e.WriteType(NT_STRING_ARRAY);
e.WriteType(NT_RPC);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(8u, e.size()-off);
ASSERT_EQ(llvm::StringRef("\x00\x01\x02\x03\x10\x11\x12\x20", 8),
llvm::StringRef(e.data(), e.size()).substr(off));
}
TEST_F(WireEncoderTest, WriteTypeError) {
WireEncoder e(0x0200u);
e.WriteType(NT_UNASSIGNED);
EXPECT_EQ(0u, e.size());
EXPECT_EQ(std::string("unrecognized type"), e.error());
e.Reset();
e.WriteType(NT_RAW);
EXPECT_EQ(0u, e.size());
EXPECT_EQ(std::string("raw type not supported in protocol < 3.0"), e.error());
e.Reset();
e.WriteType(NT_RPC);
EXPECT_EQ(0u, e.size());
EXPECT_EQ(std::string("RPC type not supported in protocol < 3.0"), e.error());
}
TEST_F(WireEncoderTest, Reset) {
WireEncoder e(0x0300u);
e.WriteType(NT_UNASSIGNED);
EXPECT_NE(nullptr, e.error());
e.Reset();
EXPECT_EQ(nullptr, e.error());
e.Write8(0u);
EXPECT_EQ(1u, e.size());
e.Reset();
EXPECT_EQ(0u, e.size());
}
TEST_F(WireEncoderTest, GetValueSize2) {
WireEncoder e(0x0200u);
EXPECT_EQ(0u, e.GetValueSize(*v_empty)); // empty
EXPECT_EQ(1u, e.GetValueSize(*v_boolean));
EXPECT_EQ(8u, e.GetValueSize(*v_double));
EXPECT_EQ(7u, e.GetValueSize(*v_string));
EXPECT_EQ(0u, e.GetValueSize(*v_raw)); // not supported
EXPECT_EQ(1u+3u, e.GetValueSize(*v_boolean_array));
// truncated
EXPECT_EQ(1u+255u, e.GetValueSize(*v_boolean_array_big));
EXPECT_EQ(1u+2u*8u, e.GetValueSize(*v_double_array));
// truncated
EXPECT_EQ(1u+255u*8u, e.GetValueSize(*v_double_array_big));
EXPECT_EQ(1u+7u+9u, e.GetValueSize(*v_string_array));
// truncated
EXPECT_EQ(1u+255u*3u, e.GetValueSize(*v_string_array_big));
}
TEST_F(WireEncoderTest, WriteBooleanValue2) {
WireEncoder e(0x0200u);
e.WriteValue(*v_boolean);
auto v_false = Value::MakeBoolean(false);
e.WriteValue(*v_false);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(2u, e.size());
ASSERT_EQ(llvm::StringRef("\x01\x00", 2),
llvm::StringRef(e.data(), e.size()));
}
TEST_F(WireEncoderTest, WriteDoubleValue2) {
WireEncoder e(0x0200u);
e.WriteValue(*v_double);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(8u, e.size());
ASSERT_EQ(llvm::StringRef("\x3f\xf0\x00\x00\x00\x00\x00\x00", 8),
llvm::StringRef(e.data(), e.size()));
}
TEST_F(WireEncoderTest, WriteStringValue2) {
WireEncoder e(0x0200u);
e.WriteValue(*v_string);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(7u, e.size());
ASSERT_EQ(llvm::StringRef("\x00\x05hello", 7),
llvm::StringRef(e.data(), e.size()));
}
TEST_F(WireEncoderTest, WriteBooleanArrayValue2) {
WireEncoder e(0x0200u);
e.WriteValue(*v_boolean_array);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+3u, e.size());
ASSERT_EQ(llvm::StringRef("\x03\x00\x01\x00", 4),
llvm::StringRef(e.data(), e.size()));
// truncated
e.Reset();
e.WriteValue(*v_boolean_array_big);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+255u, e.size());
ASSERT_EQ(llvm::StringRef("\xff\x00", 2), llvm::StringRef(e.data(), 2));
}
TEST_F(WireEncoderTest, WriteDoubleArrayValue2) {
WireEncoder e(0x0200u);
e.WriteValue(*v_double_array);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+2u*8u, e.size());
ASSERT_EQ(llvm::StringRef("\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
"\x3f\xd0\x00\x00\x00\x00\x00\x00", 17),
llvm::StringRef(e.data(), e.size()));
// truncated
e.Reset();
e.WriteValue(*v_double_array_big);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+255u*8u, e.size());
ASSERT_EQ(llvm::StringRef("\xff\x00", 2), llvm::StringRef(e.data(), 2));
}
TEST_F(WireEncoderTest, WriteStringArrayValue2) {
WireEncoder e(0x0200u);
e.WriteValue(*v_string_array);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+7u+9u, e.size());
ASSERT_EQ(llvm::StringRef("\x02\x00\x05hello\x00\x07goodbye", 17),
llvm::StringRef(e.data(), e.size()));
// truncated
e.Reset();
e.WriteValue(*v_string_array_big);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+255u*3u, e.size());
ASSERT_EQ(llvm::StringRef("\xff\x00\x01", 3), llvm::StringRef(e.data(), 3));
}
TEST_F(WireEncoderTest, WriteValueError2) {
WireEncoder e(0x0200u);
e.WriteValue(*v_empty); // empty
ASSERT_EQ(0u, e.size());
ASSERT_NE(nullptr, e.error());
e.Reset();
e.WriteValue(*v_raw); // not supported
ASSERT_EQ(0u, e.size());
ASSERT_NE(nullptr, e.error());
}
TEST_F(WireEncoderTest, GetValueSize3) {
WireEncoder e(0x0300u);
EXPECT_EQ(0u, e.GetValueSize(*v_empty)); // empty
EXPECT_EQ(1u, e.GetValueSize(*v_boolean));
EXPECT_EQ(8u, e.GetValueSize(*v_double));
EXPECT_EQ(6u, e.GetValueSize(*v_string));
EXPECT_EQ(6u, e.GetValueSize(*v_raw));
EXPECT_EQ(1u+3u, e.GetValueSize(*v_boolean_array));
// truncated
EXPECT_EQ(1u+255u, e.GetValueSize(*v_boolean_array_big));
EXPECT_EQ(1u+2u*8u, e.GetValueSize(*v_double_array));
// truncated
EXPECT_EQ(1u+255u*8u, e.GetValueSize(*v_double_array_big));
EXPECT_EQ(1u+6u+8u, e.GetValueSize(*v_string_array));
// truncated
EXPECT_EQ(1u+255u*2u, e.GetValueSize(*v_string_array_big));
}
TEST_F(WireEncoderTest, WriteBooleanValue3) {
WireEncoder e(0x0300u);
e.WriteValue(*v_boolean);
auto v_false = Value::MakeBoolean(false);
e.WriteValue(*v_false);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(2u, e.size());
ASSERT_EQ(llvm::StringRef("\x01\x00", 2),
llvm::StringRef(e.data(), e.size()));
}
TEST_F(WireEncoderTest, WriteDoubleValue3) {
WireEncoder e(0x0300u);
e.WriteValue(*v_double);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(8u, e.size());
ASSERT_EQ(llvm::StringRef("\x3f\xf0\x00\x00\x00\x00\x00\x00", 8),
llvm::StringRef(e.data(), e.size()));
}
TEST_F(WireEncoderTest, WriteStringValue3) {
WireEncoder e(0x0300u);
e.WriteValue(*v_string);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(6u, e.size());
ASSERT_EQ(llvm::StringRef("\x05hello", 6),
llvm::StringRef(e.data(), e.size()));
}
TEST_F(WireEncoderTest, WriteRawValue3) {
WireEncoder e(0x0300u);
e.WriteValue(*v_raw);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(6u, e.size());
ASSERT_EQ(llvm::StringRef("\x05hello", 6),
llvm::StringRef(e.data(), e.size()));
}
TEST_F(WireEncoderTest, WriteBooleanArrayValue3) {
WireEncoder e(0x0300u);
e.WriteValue(*v_boolean_array);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+3u, e.size());
ASSERT_EQ(llvm::StringRef("\x03\x00\x01\x00", 4),
llvm::StringRef(e.data(), e.size()));
// truncated
e.Reset();
e.WriteValue(*v_boolean_array_big);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+255u, e.size());
ASSERT_EQ(llvm::StringRef("\xff\x00", 2), llvm::StringRef(e.data(), 2));
}
TEST_F(WireEncoderTest, WriteDoubleArrayValue3) {
WireEncoder e(0x0300u);
e.WriteValue(*v_double_array);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+2u*8u, e.size());
ASSERT_EQ(llvm::StringRef("\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
"\x3f\xd0\x00\x00\x00\x00\x00\x00", 17),
llvm::StringRef(e.data(), e.size()));
// truncated
e.Reset();
e.WriteValue(*v_double_array_big);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+255u*8u, e.size());
ASSERT_EQ(llvm::StringRef("\xff\x00", 2), llvm::StringRef(e.data(), 2));
}
TEST_F(WireEncoderTest, WriteStringArrayValue3) {
WireEncoder e(0x0300u);
e.WriteValue(*v_string_array);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+6u+8u, e.size());
ASSERT_EQ(llvm::StringRef("\x02\x05hello\x07goodbye", 15),
llvm::StringRef(e.data(), e.size()));
// truncated
e.Reset();
e.WriteValue(*v_string_array_big);
ASSERT_EQ(nullptr, e.error());
ASSERT_EQ(1u+255u*2u, e.size());
ASSERT_EQ(llvm::StringRef("\xff\x01", 2), llvm::StringRef(e.data(), 2));
}
TEST_F(WireEncoderTest, WriteValueError3) {
WireEncoder e(0x0300u);
e.WriteValue(*v_empty); // empty
ASSERT_EQ(0u, e.size());
ASSERT_NE(nullptr, e.error());
}
TEST_F(WireEncoderTest, GetStringSize2) {
// 2-byte length
WireEncoder e(0x0200u);
EXPECT_EQ(7u, e.GetStringSize(s_normal));
EXPECT_EQ(130u, e.GetStringSize(s_long));
// truncated
EXPECT_EQ(65537u, e.GetStringSize(s_big));
}
TEST_F(WireEncoderTest, WriteString2) {
WireEncoder e(0x0200u);
e.WriteString(s_normal);
EXPECT_EQ(nullptr, e.error());
EXPECT_EQ(7u, e.size());
EXPECT_EQ(llvm::StringRef("\x00\x05hello", 7),
llvm::StringRef(e.data(), e.size()));
e.Reset();
e.WriteString(s_long);
EXPECT_EQ(nullptr, e.error());
ASSERT_EQ(130u, e.size());
EXPECT_EQ(llvm::StringRef("\x00\x80**", 4), llvm::StringRef(e.data(), 4));
EXPECT_EQ('*', e.data()[128]);
EXPECT_EQ('x', e.data()[129]);
// truncated
e.Reset();
e.WriteString(s_big);
EXPECT_EQ(nullptr, e.error());
ASSERT_EQ(65537u, e.size());
EXPECT_EQ(llvm::StringRef("\xff\xff**", 4), llvm::StringRef(e.data(), 4));
EXPECT_EQ('*', e.data()[65535]);
EXPECT_EQ('x', e.data()[65536]);
}
TEST_F(WireEncoderTest, GetStringSize3) {
// leb128-encoded length
WireEncoder e(0x0300u);
EXPECT_EQ(6u, e.GetStringSize(s_normal));
EXPECT_EQ(130u, e.GetStringSize(s_long));
EXPECT_EQ(65540u, e.GetStringSize(s_big));
}
TEST_F(WireEncoderTest, WriteString3) {
WireEncoder e(0x0300u);
e.WriteString(s_normal);
EXPECT_EQ(nullptr, e.error());
EXPECT_EQ(6u, e.size());
EXPECT_EQ(llvm::StringRef("\x05hello", 6),
llvm::StringRef(e.data(), e.size()));
e.Reset();
e.WriteString(s_long);
EXPECT_EQ(nullptr, e.error());
ASSERT_EQ(130u, e.size());
EXPECT_EQ(llvm::StringRef("\x80\x01**", 4), llvm::StringRef(e.data(), 4));
EXPECT_EQ('*', e.data()[128]);
EXPECT_EQ('x', e.data()[129]);
// NOT truncated
e.Reset();
e.WriteString(s_big);
EXPECT_EQ(nullptr, e.error());
ASSERT_EQ(65540u, e.size());
EXPECT_EQ(llvm::StringRef("\x81\x80\x04*", 4), llvm::StringRef(e.data(), 4));
EXPECT_EQ('*', e.data()[65536]);
EXPECT_EQ('x', e.data()[65537]);
EXPECT_EQ('x', e.data()[65538]);
EXPECT_EQ('x', e.data()[65539]);
}
} // namespace nt

View File

@@ -0,0 +1,24 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "gtest/gtest.h"
#include "Log.h"
int main(int argc, char **argv)
{
nt::Logger::GetInstance().SetLogger(
[](unsigned int level, const char* file, unsigned int line,
const char* msg) {
std::fputs(msg, stderr);
std::fputc('\n', stderr);
});
nt::Logger::GetInstance().set_min_level(0);
::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
return ret;
}