Browse Source

Added integration tests that generate and run wrapping code for both the CLI and C# backend.

pull/1/head
triton 12 years ago
parent
commit
e8ce0db9be
  1. 52
      src/Generator/Utils/TestsUtils.cs
  2. 38
      tests/Hello/Hello.Tests.cs
  3. 55
      tests/Hello/Hello.cpp
  4. 38
      tests/Hello/Hello.cs
  5. 47
      tests/Hello/Hello.h
  6. 203
      tests/Hello/Interop.h
  7. 8
      tests/Hello/premake4.lua

52
src/Generator/Utils/TestsUtils.cs

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
using System.IO;
using Cxxi.Generators;
namespace Cxxi.Utils
{
public abstract class LibraryTest : ILibrary
{
readonly string name;
readonly LanguageGeneratorKind kind;
public LibraryTest(string name, LanguageGeneratorKind kind)
{
this.name = name;
this.kind = kind;
}
public virtual void Setup(DriverOptions options)
{
options.LibraryName = name + ".Native";
options.GeneratorKind = kind;
options.OutputDir = "../gen/" + name;
options.GenerateLibraryNamespace = false;
var path = "../../../tests/" + name;
options.IncludeDirs.Add(path);
var files = Directory.EnumerateFiles(path, "*.h");
foreach(var file in files)
options.Headers.Add(Path.GetFileName(file));
}
public virtual void Preprocess(Library lib)
{
}
public virtual void Postprocess(Library lib)
{
}
public virtual void SetupPasses(Driver driver, PassBuilder passes)
{
}
public virtual void GenerateStart(TextTemplate template)
{
}
public virtual void GenerateAfterNamespaces(TextTemplate template)
{
}
}
}

38
tests/Hello/Hello.Tests.cs

@ -1,13 +1,43 @@ @@ -1,13 +1,43 @@
using System;
using NUnit.Framework;
[TestFixture]
public class HelloTests
{
public static void Main (String[] args)
[Test]
public void TestHello()
{
var hello = new Hello();
hello.PrintHello("Hello world");
Console.WriteLine("True =" + hello.test1(3, 3.0f).ToString());
Console.WriteLine("False =" + hello.test1(2, 3.0f).ToString());
Assert.That(hello.add(1, 1), Is.EqualTo(2));
Assert.That(hello.add(5, 5), Is.EqualTo(10));
Assert.IsTrue(hello.test1(3, 3.0f));
Assert.IsFalse(hello.test1(2, 3.0f));
var foo = new Foo { A = 4, B = 7 };
Assert.That(hello.AddFoo(foo), Is.EqualTo(11));
Assert.That(hello.AddFooPtr(foo), Is.EqualTo(11));
Assert.That(hello.AddFooRef(foo), Is.EqualTo(11));
var bar = new Bar { A = 4, B = 7 };
Assert.That(hello.AddBar(bar), Is.EqualTo(11));
var retFoo = hello.RetFoo(7, 2.0f);
Assert.That(retFoo.A, Is.EqualTo(7));
Assert.That(retFoo.B, Is.EqualTo(2.0));
var foo2 = new Foo2 {A = 4, B = 2, C = 3};
Assert.That(hello.AddFoo(foo2), Is.EqualTo(6));
Assert.That(hello.AddFoo2(foo2), Is.EqualTo(9));
var bar2 = new Bar2 { A = 4, B = 7, C = 3 };
Assert.That(hello.AddBar2(bar2), Is.EqualTo(14));
}
static void Main(string[] args)
{
var hello = new Hello();
}
}

55
tests/Hello/Hello.cpp

@ -1,18 +1,67 @@ @@ -1,18 +1,67 @@
#include <iostream>
#include "Hello.h"
using namespace std;
Foo::Foo()
{
}
Bar::Bar()
{
}
Hello::Hello ()
{
//cout << "Ctor!" << "\n";
}
void Hello::PrintHello(const char* s)
{
cout << "PrintHello: " << s << "\n";
//cout << "PrintHello: " << s << "\n";
}
bool Hello::test1(int i, float f)
{
return i == f;
}
int Hello::add(int a, int b)
{
return a + b;
}
int Hello::AddFoo(Foo foo)
{
return (int)(foo.A + foo.B);
}
int Hello::AddFooRef(Foo& foo)
{
return AddFoo(foo);
}
int Hello::AddFooPtr(Foo* foo)
{
return AddFoo(*foo);
}
int Hello::AddFoo2(Foo2 foo)
{
return (int)(foo.A + foo.B + foo.C);
}
int Hello::AddBar(Bar bar)
{
return (int)(bar.A + bar.B);
}
int Hello::AddBar2(Bar2 bar)
{
return (int)(bar.A + bar.B + bar.C);
}
Foo Hello::RetFoo(int a, float b)
{
Foo foo;
foo.A = a;
foo.B = b;
return foo;
}

38
tests/Hello/Hello.cs

@ -1,45 +1,19 @@ @@ -1,45 +1,19 @@
using Cxxi.Generators;
using Cxxi.Passes;
using Cxxi.Utils;
namespace Cxxi.Tests
{
class Hello : ILibrary
public class Hello : LibraryTest
{
private readonly LanguageGeneratorKind kind;
public Hello(LanguageGeneratorKind kind)
{
this.kind = kind;
}
public void Setup(DriverOptions options)
{
options.LibraryName = "Hello";
options.GeneratorKind = kind;
options.OutputDir = kind == LanguageGeneratorKind.CPlusPlusCLI ?
"cli" : "cs";
options.Headers.Add("Hello.h");
options.IncludeDirs.Add("../../../examples/Hello");
}
public void Preprocess(Library lib)
{
}
public void Postprocess(Library lib)
{
}
public void SetupPasses(Driver driver, PassBuilder p)
{
}
public void GenerateStart(TextTemplate template)
: base("Hello", kind)
{
}
public void GenerateAfterNamespaces(TextTemplate template)
public override void Preprocess(Library lib)
{
lib.SetClassAsValueType("Bar");
lib.SetClassAsValueType("Bar2");
}
static class Program

47
tests/Hello/Hello.h

@ -1,14 +1,55 @@ @@ -1,14 +1,55 @@
//#include <string>
#if defined(_MSC_VER)
#define CXXI_API __declspec(dllexport)
#else
#define CXXI_API
#endif
class CXXI_API Foo
{
public:
Foo();
int A;
float B;
};
class CXXI_API Foo2 : public Foo
{
public:
int C;
};
struct CXXI_API Bar
{
Bar();
int A;
float B;
};
struct CXXI_API Bar2 : public Bar
{
int C;
};
class CXXI_API Hello
{
public:
Hello ();
Hello ();
void PrintHello(const char* s);
bool test1(int i, float f);
int add(int a, int b);
int AddFoo(Foo);
int AddFooRef(Foo&);
int AddFooPtr(Foo*);
Foo RetFoo(int a, float b);
int AddFoo2(Foo2);
void PrintHello(const char* s);
bool test1(int i, float f);
int AddBar(Bar);
int AddBar2(Bar2);
};

203
tests/Hello/Interop.h

@ -1,203 +0,0 @@ @@ -1,203 +0,0 @@
// ------------------------------------------------------------------------------------------- //
// clix.hpp (from http://blog.nuclex-games.com/mono-dotnet/cxx-cli-string-marshaling)
//
// Marshals strings between .NET and C++ using C++/CLI (Visual C++ 2005 and later only).
// Faster and cleaner than the System::Interop method because it uses garbage collected memory.
// Use at your own leisure. No warranties whatsoever provided.
//
// Original code by Markus Ewald
// Updated version including several improvements suggested by Neil Hunt
//
// Licensed under the IBM CPL (free of charge, closed source commercial use is okay)
// ------------------------------------------------------------------------------------------- //
#pragma once
#include <string>
#include <vcclr.h>
// CLI extensions namespace
namespace clix {
/// <summary>Encoding types for strings</summary>
enum Encoding {
/// <summary>ANSI encoding</summary>
/// <remarks>
/// This is the default encoding you've most likely been using all around in C++. ANSI
/// means 8 Bit encoding with character codes depending on the system's selected code page.
/// <remarks>
E_ANSI,
/// <summary>UTF-8 encoding</summary>
/// <remarks>
/// This is the encoding commonly used for multilingual C++ strings. All ASCII characters
/// (0-127) will be represented as single bytes. Be aware that UTF-8 uses more than one
/// byte for extended characters, so std::string::length() might not reflect the actual
/// length of the string in characters if it contains any non-ASCII characters.
/// <remarks>
E_UTF8,
/// <summary>UTF-16 encoding</summary>
/// <remarks>
/// This is the suggested to be used for marshaling and the native encoding of .NET
/// strings. It is similar to UTF-8 but uses a minimum of two bytes per character, making
/// the number of bytes required for a given string better predictable. Be aware, however,
/// that UTF-16 can still use more than two bytes for a character, so std::wstring::length()
/// might not reflect the actual length of the string.
/// </remarks>
E_UTF16, E_UNICODE = E_UTF16
};
// Ignore this if you're just scanning the headers for informations!
/* All this template stuff might seem like overkill, but it is well thought out and enables
you to use a readable and convenient call while still keeping the highest possible code
efficiency due to compile-time evaluation of the required conversion path.
*/
namespace detail {
// Get C++ string type for specified encoding
template<Encoding encoding> struct StringTypeSelector;
template<> struct StringTypeSelector<E_ANSI> { typedef std::string Type; };
template<> struct StringTypeSelector<E_UTF8> { typedef std::string Type; };
template<> struct StringTypeSelector<E_UTF16> { typedef std::wstring Type; };
// Compile-time selection depending on whether a string is managed
template<typename StringType> struct IfManaged {
struct Select {
template<typename TrueType, typename FalseType>
struct Either { typedef FalseType Type; };
};
enum { Result = false };
};
template<> struct IfManaged<System::String ^> {
struct Select {
template<typename TrueType, typename FalseType>
struct Either { typedef TrueType Type; };
};
enum { Result = true };
};
// Direction of the marshaling process
enum MarshalingDirection {
CxxFromNet,
NetFromCxx
};
// The actual marshaling code
template<MarshalingDirection direction> struct StringMarshaler;
// Marshals to .NET from C++ strings
template<> struct StringMarshaler<NetFromCxx> {
template<Encoding encoding, typename SourceType>
static System::String ^marshal(const SourceType &string) {
// Constructs a std::[w]string in case someone gave us a char * to choke on
return marshalCxxString<encoding, SourceType>(string);
}
template<Encoding encoding, typename SourceType>
static System::String ^marshalCxxString(
const typename StringTypeSelector<encoding>::Type &cxxString
) {
typedef typename StringTypeSelector<encoding>::Type SourceStringType;
size_t byteCount = cxxString.length() * sizeof(SourceStringType::value_type);
if(byteCount == 0) return System::String::Empty;
// Copy the C++ string contents into a managed array of bytes
array<unsigned char> ^bytes = gcnew array<unsigned char>(byteCount);
{ pin_ptr<unsigned char> pinnedBytes = &bytes[0];
memcpy(pinnedBytes, cxxString.c_str(), byteCount);
}
// Now let one of .NET's encoding classes do the rest
return decode<encoding>(bytes);
}
private:
// Converts a byte array based on the selected encoding
template<Encoding encoding> static System::String ^decode(array<unsigned char> ^bytes);
template<> static System::String ^decode<E_ANSI>(array<unsigned char> ^bytes) {
return System::Text::Encoding::Default->GetString(bytes);
}
template<> static System::String ^decode<E_UTF8>(array<unsigned char> ^bytes) {
return System::Text::Encoding::UTF8->GetString(bytes);
}
template<> static System::String ^decode<E_UTF16>(array<unsigned char> ^bytes) {
return System::Text::Encoding::Unicode->GetString(bytes);
}
};
// Marshals to C++ strings from .NET
template<> struct StringMarshaler<CxxFromNet> {
template<Encoding encoding, typename SourceType>
static typename detail::StringTypeSelector<encoding>::Type marshal(
System::String ^string
) {
typedef typename StringTypeSelector<encoding>::Type StringType;
// First, we use .NET's encoding classes to convert the string into a byte array
array<unsigned char> ^bytes = encode<encoding>(string);
if(bytes->Length == 0) return StringType();
// Then we construct our native string from that byte array
pin_ptr<unsigned char> pinnedBytes(&bytes[0]);
return StringType(
reinterpret_cast<StringType::value_type *>(static_cast<unsigned char *>(pinnedBytes)),
bytes->Length / sizeof(StringType::value_type)
);
}
template<> static std::wstring marshal<E_UTF16, System::String ^>(
System::String ^string
) {
// We can directly accesss the characters in the managed string
pin_ptr<const wchar_t> pinnedChars(::PtrToStringChars(string));
return std::wstring(pinnedChars, string->Length);
}
private:
// Converts a string based on the selected encoding
template<Encoding encoding> static array<unsigned char> ^encode(System::String ^string);
template<> static array<unsigned char> ^encode<E_ANSI>(System::String ^string) {
return System::Text::Encoding::Default->GetBytes(string);
}
template<> static array<unsigned char> ^encode<E_UTF8>(System::String ^string) {
return System::Text::Encoding::UTF8->GetBytes(string);
}
template<> static array<unsigned char> ^encode<E_UTF16>(System::String ^string) {
return System::Text::Encoding::Unicode->GetBytes(string);
}
};
} // namespace detail
// ----------------------------------------------------------------------------------------- //
// clix::marshalString()
// ----------------------------------------------------------------------------------------- //
/// <summary>Marshals strings between .NET managed and C++ native</summary>
/// <remarks>
/// This all-in-one function marshals native C++ strings to .NET strings and vice versa.
/// You have to specify an encoding to use for the conversion, which always applies to the
/// native C++ string as .NET always uses UTF-16 for its own strings.
/// </remarks>
/// <param name="string">String to be marshalled to the other side</param>
/// <returns>The marshaled representation of the string</returns>
template<Encoding encoding, typename SourceType>
typename detail::IfManaged<SourceType>::Select::Either<
typename detail::StringTypeSelector<encoding>::Type,
System::String ^
>::Type marshalString(SourceType string) {
// Pass on the call to our nifty template routines
return detail::StringMarshaler<
detail::IfManaged<SourceType>::Result ? detail::CxxFromNet : detail::NetFromCxx
>::marshal<encoding, SourceType>(string);
}
} // namespace clix

8
tests/Hello/premake4.lua

@ -1,6 +1,2 @@ @@ -1,6 +1,2 @@
group "Hello"
SetupTestGeneratorProject("Hello", "Hello.cs")
SetupTestNativeProject("Hello.Native", { "Hello.cpp", "Hello.h" })
SetupTestProjects("Hello", { "Hello.Tests.cs"})
group "Tests/Hello"
SetupTestProject("Hello")
Loading…
Cancel
Save