pull/2151/head
Siddharth Rakesh 10 years ago
commit 668f33c6f3
  1. 11
      src/compiler/cpp_generator.cc
  2. 105
      src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs
  3. 6
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  4. 75
      src/csharp/Grpc.Core.Tests/Internal/ChannelArgsSafeHandleTest.cs
  5. 40
      src/csharp/Grpc.Core/Channel.cs
  6. 115
      src/csharp/Grpc.Core/ChannelArgs.cs
  7. 178
      src/csharp/Grpc.Core/ChannelOptions.cs
  8. 6
      src/csharp/Grpc.Core/Grpc.Core.csproj
  9. 8
      src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs
  10. 4
      src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
  11. 18
      src/csharp/Grpc.Core/Server.cs
  12. 10
      src/csharp/Grpc.IntegrationTesting/InteropClient.cs
  13. 9
      src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
  14. 10
      src/csharp/ext/grpc_csharp_ext.c
  15. 137
      src/php/README.md
  16. 4
      src/php/bin/run_gen_code_test.sh
  17. 7
      src/php/composer.json
  18. 2
      src/php/tests/generated_code/AbstractGeneratedCodeTest.php
  19. 2
      src/php/tests/interop/interop_client.php
  20. 13
      src/python/src/grpc/_adapter/_c/utility.c
  21. 132
      test/compiler/python_plugin_test.py
  22. 2
      tools/run_tests/build_python.sh
  23. 16
      tools/run_tests/jobset.py
  24. 9
      tools/run_tests/run_tests.py

@ -849,6 +849,9 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
"::grpc::Status $ns$$Service$::Service::$Method$(" "::grpc::Status $ns$$Service$::Service::$Method$("
"::grpc::ServerContext* context, " "::grpc::ServerContext* context, "
"const $Request$* request, $Response$* response) {\n"); "const $Request$* request, $Response$* response) {\n");
printer->Print(" (void) context;\n");
printer->Print(" (void) request;\n");
printer->Print(" (void) response;\n");
printer->Print( printer->Print(
" return ::grpc::Status(" " return ::grpc::Status("
"::grpc::StatusCode::UNIMPLEMENTED);\n"); "::grpc::StatusCode::UNIMPLEMENTED);\n");
@ -859,6 +862,9 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
"::grpc::ServerContext* context, " "::grpc::ServerContext* context, "
"::grpc::ServerReader< $Request$>* reader, " "::grpc::ServerReader< $Request$>* reader, "
"$Response$* response) {\n"); "$Response$* response) {\n");
printer->Print(" (void) context;\n");
printer->Print(" (void) reader;\n");
printer->Print(" (void) response;\n");
printer->Print( printer->Print(
" return ::grpc::Status(" " return ::grpc::Status("
"::grpc::StatusCode::UNIMPLEMENTED);\n"); "::grpc::StatusCode::UNIMPLEMENTED);\n");
@ -869,6 +875,9 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
"::grpc::ServerContext* context, " "::grpc::ServerContext* context, "
"const $Request$* request, " "const $Request$* request, "
"::grpc::ServerWriter< $Response$>* writer) {\n"); "::grpc::ServerWriter< $Response$>* writer) {\n");
printer->Print(" (void) context;\n");
printer->Print(" (void) request;\n");
printer->Print(" (void) writer;\n");
printer->Print( printer->Print(
" return ::grpc::Status(" " return ::grpc::Status("
"::grpc::StatusCode::UNIMPLEMENTED);\n"); "::grpc::StatusCode::UNIMPLEMENTED);\n");
@ -879,6 +888,8 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
"::grpc::ServerContext* context, " "::grpc::ServerContext* context, "
"::grpc::ServerReaderWriter< $Response$, $Request$>* " "::grpc::ServerReaderWriter< $Response$, $Request$>* "
"stream) {\n"); "stream) {\n");
printer->Print(" (void) context;\n");
printer->Print(" (void) stream;\n");
printer->Print( printer->Print(
" return ::grpc::Status(" " return ::grpc::Status("
"::grpc::StatusCode::UNIMPLEMENTED);\n"); "::grpc::StatusCode::UNIMPLEMENTED);\n");

@ -0,0 +1,105 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class ChannelOptionsTest
{
[Test]
public void IntOption()
{
var option = new ChannelOption("somename", 1);
Assert.AreEqual(ChannelOption.OptionType.Integer, option.Type);
Assert.AreEqual("somename", option.Name);
Assert.AreEqual(1, option.IntValue);
Assert.Throws(typeof(InvalidOperationException), () => { var s = option.StringValue; });
}
[Test]
public void StringOption()
{
var option = new ChannelOption("somename", "ABCDEF");
Assert.AreEqual(ChannelOption.OptionType.String, option.Type);
Assert.AreEqual("somename", option.Name);
Assert.AreEqual("ABCDEF", option.StringValue);
Assert.Throws(typeof(InvalidOperationException), () => { var s = option.IntValue; });
}
[Test]
public void ConstructorPreconditions()
{
Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption(null, "abc"); });
Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption(null, 1); });
Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption("abc", null); });
}
[Test]
public void CreateChannelArgsNull()
{
var channelArgs = ChannelOptions.CreateChannelArgs(null);
Assert.IsTrue(channelArgs.IsInvalid);
}
[Test]
public void CreateChannelArgsEmpty()
{
var options = new List<ChannelOption>();
var channelArgs = ChannelOptions.CreateChannelArgs(options);
channelArgs.Dispose();
}
[Test]
public void CreateChannelArgs()
{
var options = new List<ChannelOption>
{
new ChannelOption("ABC", "XYZ"),
new ChannelOption("somename", "IJKLM"),
new ChannelOption("intoption", 12345),
new ChannelOption("GHIJK", 12345),
};
var channelArgs = ChannelOptions.CreateChannelArgs(options);
channelArgs.Dispose();
}
}
}

@ -3,8 +3,6 @@
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{86EC5CB4-4EA2-40A2-8057-86542A0353BB}</ProjectGuid> <ProjectGuid>{86EC5CB4-4EA2-40A2-8057-86542A0353BB}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RootNamespace>Grpc.Core.Tests</RootNamespace> <RootNamespace>Grpc.Core.Tests</RootNamespace>
@ -48,6 +46,8 @@
<Compile Include="Internal\MetadataArraySafeHandleTest.cs" /> <Compile Include="Internal\MetadataArraySafeHandleTest.cs" />
<Compile Include="Internal\CompletionQueueSafeHandleTest.cs" /> <Compile Include="Internal\CompletionQueueSafeHandleTest.cs" />
<Compile Include="Internal\CompletionQueueEventTest.cs" /> <Compile Include="Internal\CompletionQueueEventTest.cs" />
<Compile Include="Internal\ChannelArgsSafeHandleTest.cs" />
<Compile Include="ChannelOptionsTest.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup> <ItemGroup>
@ -63,4 +63,4 @@
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
</Project> </Project>

@ -0,0 +1,75 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class ChannelArgsSafeHandleTest
{
[Test]
public void CreateEmptyAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.Create(0);
channelArgs.Dispose();
}
[Test]
public void CreateNonEmptyAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.Create(5);
channelArgs.Dispose();
}
[Test]
public void CreateNullAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.CreateNull();
channelArgs.Dispose();
}
[Test]
public void CreateFillAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.Create(3);
channelArgs.SetInteger(0, "somekey", 12345);
channelArgs.SetString(1, "somekey", "abcdefghijkl");
channelArgs.SetString(2, "somekey", "XYZ");
channelArgs.Dispose();
}
}
}

@ -29,6 +29,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -50,10 +51,10 @@ namespace Grpc.Core
/// </summary> /// </summary>
/// <param name="host">The DNS name of IP address of the host.</param> /// <param name="host">The DNS name of IP address of the host.</param>
/// <param name="credentials">Optional credentials to create a secure channel.</param> /// <param name="credentials">Optional credentials to create a secure channel.</param>
/// <param name="channelArgs">Optional channel arguments.</param> /// <param name="options">Channel options.</param>
public Channel(string host, Credentials credentials = null, ChannelArgs channelArgs = null) public Channel(string host, Credentials credentials = null, IEnumerable<ChannelOption> options = null)
{ {
using (ChannelArgsSafeHandle nativeChannelArgs = CreateNativeChannelArgs(channelArgs)) using (ChannelArgsSafeHandle nativeChannelArgs = ChannelOptions.CreateChannelArgs(options))
{ {
if (credentials != null) if (credentials != null)
{ {
@ -67,7 +68,7 @@ namespace Grpc.Core
this.handle = ChannelSafeHandle.Create(host, nativeChannelArgs); this.handle = ChannelSafeHandle.Create(host, nativeChannelArgs);
} }
} }
this.target = GetOverridenTarget(host, channelArgs); this.target = GetOverridenTarget(host, options);
} }
/// <summary> /// <summary>
@ -76,9 +77,9 @@ namespace Grpc.Core
/// <param name="host">DNS name or IP address</param> /// <param name="host">DNS name or IP address</param>
/// <param name="port">the port</param> /// <param name="port">the port</param>
/// <param name="credentials">Optional credentials to create a secure channel.</param> /// <param name="credentials">Optional credentials to create a secure channel.</param>
/// <param name="channelArgs">Optional channel arguments.</param> /// <param name="options">Channel options.</param>
public Channel(string host, int port, Credentials credentials = null, ChannelArgs channelArgs = null) : public Channel(string host, int port, Credentials credentials = null, IEnumerable<ChannelOption> options = null) :
this(string.Format("{0}:{1}", host, port), credentials, channelArgs) this(string.Format("{0}:{1}", host, port), credentials, options)
{ {
} }
@ -112,22 +113,25 @@ namespace Grpc.Core
} }
} }
private static string GetOverridenTarget(string target, ChannelArgs args) /// <summary>
/// Look for SslTargetNameOverride option and return its value instead of originalTarget
/// if found.
/// </summary>
private static string GetOverridenTarget(string originalTarget, IEnumerable<ChannelOption> options)
{ {
if (args != null && !string.IsNullOrEmpty(args.GetSslTargetNameOverride())) if (options == null)
{ {
return args.GetSslTargetNameOverride(); return originalTarget;
} }
return target; foreach (var option in options)
}
private static ChannelArgsSafeHandle CreateNativeChannelArgs(ChannelArgs args)
{
if (args == null)
{ {
return ChannelArgsSafeHandle.CreateNull(); if (option.Type == ChannelOption.OptionType.String
&& option.Name == ChannelOptions.SslTargetNameOverride)
{
return option.StringValue;
}
} }
return args.ToNativeChannelArgs(); return originalTarget;
} }
} }
} }

@ -1,115 +0,0 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core.Internal;
namespace Grpc.Core
{
/// <summary>
/// gRPC channel options.
/// </summary>
public class ChannelArgs
{
public const string SslTargetNameOverrideKey = "grpc.ssl_target_name_override";
readonly ImmutableDictionary<string, string> stringArgs;
private ChannelArgs(ImmutableDictionary<string, string> stringArgs)
{
this.stringArgs = stringArgs;
}
public string GetSslTargetNameOverride()
{
string result;
if (stringArgs.TryGetValue(SslTargetNameOverrideKey, out result))
{
return result;
}
return null;
}
public static Builder CreateBuilder()
{
return new Builder();
}
public class Builder
{
readonly Dictionary<string, string> stringArgs = new Dictionary<string, string>();
// TODO: AddInteger not supported yet.
public Builder AddString(string key, string value)
{
stringArgs.Add(key, value);
return this;
}
public ChannelArgs Build()
{
return new ChannelArgs(stringArgs.ToImmutableDictionary());
}
}
/// <summary>
/// Creates native object for the channel arguments.
/// </summary>
/// <returns>The native channel arguments.</returns>
internal ChannelArgsSafeHandle ToNativeChannelArgs()
{
ChannelArgsSafeHandle nativeArgs = null;
try
{
nativeArgs = ChannelArgsSafeHandle.Create(stringArgs.Count);
int i = 0;
foreach (var entry in stringArgs)
{
nativeArgs.SetString(i, entry.Key, entry.Value);
i++;
}
return nativeArgs;
}
catch (Exception)
{
if (nativeArgs != null)
{
nativeArgs.Dispose();
}
throw;
}
}
}
}

@ -0,0 +1,178 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Channel option specified when creating a channel.
/// Corresponds to grpc_channel_args from grpc/grpc.h.
/// </summary>
public sealed class ChannelOption
{
public enum OptionType
{
Integer,
String
}
private readonly OptionType type;
private readonly string name;
private readonly int intValue;
private readonly string stringValue;
/// <summary>
/// Creates a channel option with a string value.
/// </summary>
/// <param name="name">Name.</param>
/// <param name="stringValue">String value.</param>
public ChannelOption(string name, string stringValue)
{
this.type = OptionType.String;
this.name = Preconditions.CheckNotNull(name);
this.stringValue = Preconditions.CheckNotNull(stringValue);
}
/// <summary>
/// Creates a channel option with an integer value.
/// </summary>
/// <param name="name">Name.</param>
/// <param name="stringValue">String value.</param>
public ChannelOption(string name, int intValue)
{
this.type = OptionType.Integer;
this.name = Preconditions.CheckNotNull(name);
this.intValue = intValue;
}
public OptionType Type
{
get
{
return type;
}
}
public string Name
{
get
{
return name;
}
}
public int IntValue
{
get
{
Preconditions.CheckState(type == OptionType.Integer);
return intValue;
}
}
public string StringValue
{
get
{
Preconditions.CheckState(type == OptionType.String);
return stringValue;
}
}
}
public static class ChannelOptions
{
// Override SSL target check. Only to be used for testing.
public const string SslTargetNameOverride = "grpc.ssl_target_name_override";
// Enable census for tracing and stats collection
public const string Census = "grpc.census";
// Maximum number of concurrent incoming streams to allow on a http2 connection
public const string MaxConcurrentStreams = "grpc.max_concurrent_streams";
// Maximum message length that the channel can receive
public const string MaxMessageLength = "grpc.max_message_length";
// Initial sequence number for http2 transports
public const string Http2InitialSequenceNumber = "grpc.http2.initial_sequence_number";
/// <summary>
/// Creates native object for a collection of channel options.
/// </summary>
/// <returns>The native channel arguments.</returns>
internal static ChannelArgsSafeHandle CreateChannelArgs(IEnumerable<ChannelOption> options)
{
if (options == null)
{
return ChannelArgsSafeHandle.CreateNull();
}
var optionList = new List<ChannelOption>(options); // It's better to do defensive copy
ChannelArgsSafeHandle nativeArgs = null;
try
{
nativeArgs = ChannelArgsSafeHandle.Create(optionList.Count);
for (int i = 0; i < optionList.Count; i++)
{
var option = optionList[i];
if (option.Type == ChannelOption.OptionType.Integer)
{
nativeArgs.SetInteger(i, option.Name, option.IntValue);
}
else if (option.Type == ChannelOption.OptionType.String)
{
nativeArgs.SetString(i, option.Name, option.StringValue);
}
else
{
throw new InvalidOperationException("Unknown option type");
}
}
return nativeArgs;
}
catch (Exception)
{
if (nativeArgs != null)
{
nativeArgs.Dispose();
}
throw;
}
}
}
}

@ -5,8 +5,6 @@
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</ProjectGuid> <ProjectGuid>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RootNamespace>Grpc.Core</RootNamespace> <RootNamespace>Grpc.Core</RootNamespace>
@ -78,7 +76,6 @@
<Compile Include="Internal\CredentialsSafeHandle.cs" /> <Compile Include="Internal\CredentialsSafeHandle.cs" />
<Compile Include="Credentials.cs" /> <Compile Include="Credentials.cs" />
<Compile Include="Internal\ChannelArgsSafeHandle.cs" /> <Compile Include="Internal\ChannelArgsSafeHandle.cs" />
<Compile Include="ChannelArgs.cs" />
<Compile Include="Internal\AsyncCompletion.cs" /> <Compile Include="Internal\AsyncCompletion.cs" />
<Compile Include="Internal\AsyncCallBase.cs" /> <Compile Include="Internal\AsyncCallBase.cs" />
<Compile Include="Internal\AsyncCallServer.cs" /> <Compile Include="Internal\AsyncCallServer.cs" />
@ -103,6 +100,7 @@
<Compile Include="Internal\CompletionQueueEvent.cs" /> <Compile Include="Internal\CompletionQueueEvent.cs" />
<Compile Include="Internal\CompletionRegistry.cs" /> <Compile Include="Internal\CompletionRegistry.cs" />
<Compile Include="Internal\BatchContextSafeHandle.cs" /> <Compile Include="Internal\BatchContextSafeHandle.cs" />
<Compile Include="ChannelOptions.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
@ -132,4 +130,4 @@
</Target> </Target>
<Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets')" /> <Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets')" />
<Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets')" /> <Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets')" />
</Project> </Project>

@ -45,6 +45,9 @@ namespace Grpc.Core.Internal
[DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)] [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
static extern void grpcsharp_channel_args_set_string(ChannelArgsSafeHandle args, UIntPtr index, string key, string value); static extern void grpcsharp_channel_args_set_string(ChannelArgsSafeHandle args, UIntPtr index, string key, string value);
[DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
static extern void grpcsharp_channel_args_set_integer(ChannelArgsSafeHandle args, UIntPtr index, string key, int value);
[DllImport("grpc_csharp_ext.dll")] [DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_channel_args_destroy(IntPtr args); static extern void grpcsharp_channel_args_destroy(IntPtr args);
@ -67,6 +70,11 @@ namespace Grpc.Core.Internal
grpcsharp_channel_args_set_string(this, new UIntPtr((uint)index), key, value); grpcsharp_channel_args_set_string(this, new UIntPtr((uint)index), key, value);
} }
public void SetInteger(int index, string key, int value)
{
grpcsharp_channel_args_set_integer(this, new UIntPtr((uint)index), key, value);
}
protected override bool ReleaseHandle() protected override bool ReleaseHandle()
{ {
grpcsharp_channel_args_destroy(handle); grpcsharp_channel_args_destroy(handle);

@ -45,7 +45,7 @@ namespace Grpc.Core.Internal
internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid
{ {
[DllImport("grpc_csharp_ext.dll")] [DllImport("grpc_csharp_ext.dll")]
static extern ServerSafeHandle grpcsharp_server_create(CompletionQueueSafeHandle cq, IntPtr args); static extern ServerSafeHandle grpcsharp_server_create(CompletionQueueSafeHandle cq, ChannelArgsSafeHandle args);
[DllImport("grpc_csharp_ext.dll")] [DllImport("grpc_csharp_ext.dll")]
static extern int grpcsharp_server_add_http2_port(ServerSafeHandle server, string addr); static extern int grpcsharp_server_add_http2_port(ServerSafeHandle server, string addr);
@ -72,7 +72,7 @@ namespace Grpc.Core.Internal
{ {
} }
public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, IntPtr args) public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, ChannelArgsSafeHandle args)
{ {
return grpcsharp_server_create(cq, args); return grpcsharp_server_create(cq, args);
} }

@ -52,9 +52,6 @@ namespace Grpc.Core
/// </summary> /// </summary>
public const int PickUnusedPort = 0; public const int PickUnusedPort = 0;
//readonly OpCompletionDelegate serverShutdownHandler;
//readonly OpCompletionDelegate newServerRpcHandler;
readonly ServerSafeHandle handle; readonly ServerSafeHandle handle;
readonly object myLock = new object(); readonly object myLock = new object();
@ -64,11 +61,16 @@ namespace Grpc.Core
bool startRequested; bool startRequested;
bool shutdownRequested; bool shutdownRequested;
public Server() /// <summary>
/// Create a new server.
/// </summary>
/// <param name="options">Channel options.</param>
public Server(IEnumerable<ChannelOption> options = null)
{ {
this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), IntPtr.Zero); using (var channelArgs = ChannelOptions.CreateChannelArgs(options))
//this.newServerRpcHandler = HandleNewServerRpc; {
//this.serverShutdownHandler = HandleServerShutdown; this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), channelArgs);
}
} }
/// <summary> /// <summary>
@ -141,8 +143,6 @@ namespace Grpc.Core
Preconditions.CheckState(!shutdownRequested); Preconditions.CheckState(!shutdownRequested);
shutdownRequested = true; shutdownRequested = true;
} }
var ctx = BatchContextSafeHandle.Create();
handle.ShutdownAndNotify(HandleServerShutdown); handle.ShutdownAndNotify(HandleServerShutdown);
await shutdownTcs.Task; await shutdownTcs.Task;
handle.Dispose(); handle.Dispose();

@ -110,14 +110,16 @@ namespace Grpc.IntegrationTesting
credentials = TestCredentials.CreateTestClientCredentials(options.useTestCa); credentials = TestCredentials.CreateTestClientCredentials(options.useTestCa);
} }
ChannelArgs channelArgs = null; List<ChannelOption> channelOptions = null;
if (!string.IsNullOrEmpty(options.serverHostOverride)) if (!string.IsNullOrEmpty(options.serverHostOverride))
{ {
channelArgs = ChannelArgs.CreateBuilder() channelOptions = new List<ChannelOption>
.AddString(ChannelArgs.SslTargetNameOverrideKey, options.serverHostOverride).Build(); {
new ChannelOption(ChannelOptions.SslTargetNameOverride, options.serverHostOverride)
};
} }
using (Channel channel = new Channel(options.serverHost, options.serverPort.Value, credentials, channelArgs)) using (Channel channel = new Channel(options.serverHost, options.serverPort.Value, credentials, channelOptions))
{ {
var stubConfig = StubConfiguration.Default; var stubConfig = StubConfiguration.Default;
if (options.testCase == "service_account_creds" || options.testCase == "compute_engine_creds") if (options.testCase == "service_account_creds" || options.testCase == "compute_engine_creds")

@ -62,10 +62,11 @@ namespace Grpc.IntegrationTesting
int port = server.AddListeningPort(host, Server.PickUnusedPort, TestCredentials.CreateTestServerCredentials()); int port = server.AddListeningPort(host, Server.PickUnusedPort, TestCredentials.CreateTestServerCredentials());
server.Start(); server.Start();
var channelArgs = ChannelArgs.CreateBuilder() var options = new List<ChannelOption>
.AddString(ChannelArgs.SslTargetNameOverrideKey, TestCredentials.DefaultHostOverride).Build(); {
new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
channel = new Channel(host, port, TestCredentials.CreateTestClientCredentials(true), channelArgs); };
channel = new Channel(host, port, TestCredentials.CreateTestClientCredentials(true), options);
client = TestService.NewStub(channel); client = TestService.NewStub(channel);
} }

@ -354,6 +354,16 @@ grpcsharp_channel_args_set_string(grpc_channel_args *args, size_t index,
args->args[index].value.string = gpr_strdup(value); args->args[index].value.string = gpr_strdup(value);
} }
GPR_EXPORT void GPR_CALLTYPE
grpcsharp_channel_args_set_integer(grpc_channel_args *args, size_t index,
const char *key, int value) {
GPR_ASSERT(args);
GPR_ASSERT(index < args->num_args);
args->args[index].type = GRPC_ARG_INTEGER;
args->args[index].key = gpr_strdup(key);
args->args[index].value.integer = value;
}
GPR_EXPORT void GPR_CALLTYPE GPR_EXPORT void GPR_CALLTYPE
grpcsharp_channel_args_destroy(grpc_channel_args *args) { grpcsharp_channel_args_destroy(grpc_channel_args *args) {
size_t i; size_t i;

@ -7,51 +7,122 @@ This directory contains source code for PHP implementation of gRPC layered on sh
Pre-Alpha : This gRPC PHP implementation is work-in-progress and is not expected to work yet. Pre-Alpha : This gRPC PHP implementation is work-in-progress and is not expected to work yet.
## LAYOUT
Directory structure is as generated by the PHP utility
[ext_skel](http://php.net/manual/en/internals2.buildsys.skeleton.php)
## ENVIRONMENT ## ENVIRONMENT
Install `php5` and `php5-dev`. Install `php5` and `php5-dev`.
To run the tests, additionally install `php5-readline` and `phpunit`. To run the tests, additionally install `phpunit`.
Alternatively, build and install PHP 5.5 or later from source with standard Alternatively, build and install PHP 5.5 or later from source with standard
configuration options. configuration options.
To also download and install protoc and the PHP code generator. ## Build from Homebrew
On Mac OS X, install [homebrew][]. On Linux, install [linuxbrew][]. Run the following command to
install gRPC.
```sh
$ curl -fsSL https://goo.gl/getgrpc | bash -s php
```
This will download and run the [gRPC install script][] and compile the gRPC PHP extension.
## Build from Source
Clone this repository
```
$ git clone https://github.com/grpc/grpc.git
```
Build and install the Protocol Buffers compiler (protoc)
```
$ cd grpc
$ git pull --recurse-submodules && git submodule update --init --recursive
$ cd third_party/protobuf
$ ./autogen.sh
$ ./configure
$ make
$ make check
$ sudo make install
```
Build and install the gRPC C core
```sh
$ cd grpc
$ make
$ sudo make install
```
Build the gRPC PHP extension
```bash ```sh
apt-get install -y procps $ cd grpc/src/php/ext/grpc
curl -sSL https://get.rvm.io | sudo bash -s stable --ruby $ phpize
git clone git@github.com:google/protobuf.git $ ./configure
cd protobuf $ make
./configure $ sudo make install
make
make install
git clone git@github.com:murgatroid99/Protobuf-PHP.git
cd Protobuf-PHP
rake pear:package version=1.0
pear install Protobuf-1.0.tgz
``` ```
## BUILDING In your php.ini file, add the line `extension=grpc.so` to load the extension
at PHP startup.
1. In ./ext/grpc, run the command `phpize` (distributed with PHP) Install Composer
2. Run `./ext/grpc/configure`
3. In ./ext/grpc, run `make` and `sudo make install`
4. In your php.ini file, add the line `extension=grpc.so` to load the
extension at PHP startup.
## PHPUnit ```sh
$ cd grpc/src/php
$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar install
```
## Unit Tests
Run unit tests
```sh
$ cd grpc/src/php
$ ./bin/run_tests.sh
```
## Generated Code Tests
Install `protoc-gen-php`
```sh
$ cd grpc/src/php/vendor/datto/protobuf-php
$ gem install rake ronn
$ rake pear:package version=1.0
$ sudo pear install Protobuf-1.0.tgz
```
Generate client stub code
```sh
$ cd grpc/src/php
$ ./bin/generate_proto_php.sh
```
Run a local server serving the math services
- Please see [Node][] on how to run an example server
```sh
$ cd grpc/src/node
$ npm install
$ nodejs examples/math_server.js
```
Run the generated code tests
```sh
$ cd grpc/src/php
$ ./bin/run_gen_code_test.sh
```
This repo now has PHPUnit tests, which can by run by executing [homebrew]:http://brew.sh
`./bin/run_tests.sh` after building. [linuxbrew]:https://github.com/Homebrew/linuxbrew#installation
[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
[Node]:https://github.com/grpc/grpc/tree/master/src/node/examples
There is also a generated code test (`./bin/run_gen_code_test.sh`), which tests
the stub `./tests/generated_code/math.php` against a running localhost server
serving the math service. That stub is generated from
`./tests/generated_code/math.proto`.

@ -30,8 +30,8 @@
cd $(dirname $0) cd $(dirname $0)
GRPC_TEST_HOST=localhost:50051 php -d extension_dir=../ext/grpc/modules/ \ GRPC_TEST_HOST=localhost:50051 php -d extension_dir=../ext/grpc/modules/ \
-d extension=grpc.so /usr/local/bin/phpunit -v --debug --strict \ -d extension=grpc.so `which phpunit` -v --debug --strict \
../tests/generated_code/GeneratedCodeTest.php ../tests/generated_code/GeneratedCodeTest.php
GRPC_TEST_HOST=localhost:50051 php -d extension_dir=../ext/grpc/modules/ \ GRPC_TEST_HOST=localhost:50051 php -d extension_dir=../ext/grpc/modules/ \
-d extension=grpc.so /usr/local/bin/phpunit -v --debug --strict \ -d extension=grpc.so `which phpunit` -v --debug --strict \
../tests/generated_code/GeneratedCodeWithCallbackTest.php ../tests/generated_code/GeneratedCodeWithCallbackTest.php

@ -4,8 +4,15 @@
"version": "0.5.0", "version": "0.5.0",
"homepage": "http://grpc.io", "homepage": "http://grpc.io",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"repositories": [
{
"type": "vcs",
"url": "https://github.com/stanley-cheung/Protobuf-PHP"
}
],
"require": { "require": {
"php": ">=5.5.0", "php": ">=5.5.0",
"datto/protobuf-php": "dev-master",
"google/auth": "dev-master" "google/auth": "dev-master"
}, },
"autoload": { "autoload": {

@ -32,8 +32,6 @@
* *
*/ */
require_once realpath(dirname(__FILE__) . '/../../vendor/autoload.php'); require_once realpath(dirname(__FILE__) . '/../../vendor/autoload.php');
require 'DrSlump/Protobuf.php';
\DrSlump\Protobuf::autoload();
require 'math.php'; require 'math.php';
abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase { abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase {
/* These tests require that a server exporting the math service must be /* These tests require that a server exporting the math service must be

@ -32,8 +32,6 @@
* *
*/ */
require_once realpath(dirname(__FILE__) . '/../../vendor/autoload.php'); require_once realpath(dirname(__FILE__) . '/../../vendor/autoload.php');
require 'DrSlump/Protobuf.php';
\DrSlump\Protobuf::autoload();
require 'empty.php'; require 'empty.php';
require 'message_set.php'; require 'message_set.php';
require 'messages.php'; require 'messages.php';

@ -40,6 +40,7 @@
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/slice.h> #include <grpc/support/slice.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include <grpc/support/string_util.h>
#include "grpc/_adapter/_c/types.h" #include "grpc/_adapter/_c/types.h"
@ -156,9 +157,10 @@ int pygrpc_produce_op(PyObject *op, grpc_op *result) {
return 0; return 0;
} }
if (PyTuple_Size(op) != OP_TUPLE_SIZE) { if (PyTuple_Size(op) != OP_TUPLE_SIZE) {
char buf[64]; char *buf;
snprintf(buf, sizeof(buf), "expected tuple op of length %d", OP_TUPLE_SIZE); gpr_asprintf(&buf, "expected tuple op of length %d", OP_TUPLE_SIZE);
PyErr_SetString(PyExc_ValueError, buf); PyErr_SetString(PyExc_ValueError, buf);
gpr_free(buf);
return 0; return 0;
} }
type = PyInt_AsLong(PyTuple_GET_ITEM(op, TYPE_INDEX)); type = PyInt_AsLong(PyTuple_GET_ITEM(op, TYPE_INDEX));
@ -353,9 +355,14 @@ double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec) {
return timespec.tv_sec + 1e-9*timespec.tv_nsec; return timespec.tv_sec + 1e-9*timespec.tv_nsec;
} }
/* Because C89 doesn't have a way to check for infinity... */
static int pygrpc_isinf(double x) {
return x * 0 != 0;
}
gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds) { gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds) {
gpr_timespec result; gpr_timespec result;
if isinf(seconds) { if (pygrpc_isinf(seconds)) {
result = seconds > 0.0 ? gpr_inf_future : gpr_inf_past; result = seconds > 0.0 ? gpr_inf_future : gpr_inf_past;
} else { } else {
result.tv_sec = (time_t)seconds; result.tv_sec = (time_t)seconds;

@ -36,6 +36,7 @@ import shutil
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
import threading
import time import time
import unittest import unittest
@ -49,13 +50,13 @@ STUB_IDENTIFIER = 'EarlyAdopterTestServiceStub'
SERVER_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_server' SERVER_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_server'
STUB_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_stub' STUB_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_stub'
# Timeouts and delays. # The timeout used in tests of RPCs that are supposed to expire.
SHORT_TIMEOUT = 0.1 SHORT_TIMEOUT = 2
NORMAL_TIMEOUT = 1 # The timeout used in tests of RPCs that are not supposed to expire. The
LONG_TIMEOUT = 2 # absurdly large value doesn't matter since no passing execution of this test
DOES_NOT_MATTER_DELAY = 0 # module will ever wait the duration.
LONG_TIMEOUT = 600
NO_DELAY = 0 NO_DELAY = 0
LONG_DELAY = 1
# Build mode environment variable set by tools/run_tests/run_tests.py. # Build mode environment variable set by tools/run_tests/run_tests.py.
_build_mode = os.environ['CONFIG'] _build_mode = os.environ['CONFIG']
@ -64,29 +65,36 @@ _build_mode = os.environ['CONFIG']
class _ServicerMethods(object): class _ServicerMethods(object):
def __init__(self, test_pb2, delay): def __init__(self, test_pb2, delay):
self._condition = threading.Condition()
self._delay = delay
self._paused = False self._paused = False
self._failed = False self._fail = False
self._test_pb2 = test_pb2 self._test_pb2 = test_pb2
self._delay = delay
@contextlib.contextmanager @contextlib.contextmanager
def pause(self): # pylint: disable=invalid-name def pause(self): # pylint: disable=invalid-name
self._paused = True with self._condition:
self._paused = True
yield yield
self._paused = False with self._condition:
self._paused = False
self._condition.notify_all()
@contextlib.contextmanager @contextlib.contextmanager
def fail(self): # pylint: disable=invalid-name def fail(self): # pylint: disable=invalid-name
self._failed = True with self._condition:
self._fail = True
yield yield
self._failed = False with self._condition:
self._fail = False
def _control(self): # pylint: disable=invalid-name def _control(self): # pylint: disable=invalid-name
if self._failed: with self._condition:
raise ValueError() if self._fail:
raise ValueError()
while self._paused:
self._condition.wait()
time.sleep(self._delay) time.sleep(self._delay)
while self._paused:
time.sleep(0)
def UnaryCall(self, request, unused_rpc_context): def UnaryCall(self, request, unused_rpc_context):
response = self._test_pb2.SimpleResponse() response = self._test_pb2.SimpleResponse()
@ -147,9 +155,8 @@ def _CreateService(test_pb2, delay):
waiting for the service. waiting for the service.
Args: Args:
test_pb2: the test_pb2 module generated by this test test_pb2: The test_pb2 module generated by this test.
delay: delay in seconds per response from the servicer delay: Delay in seconds per response from the servicer.
timeout: how long the stub will wait for the servicer by default.
Yields: Yields:
A (servicer_methods, servicer, stub) three-tuple where servicer_methods is A (servicer_methods, servicer, stub) three-tuple where servicer_methods is
@ -250,7 +257,7 @@ class PythonPluginTest(unittest.TestCase):
if exc.errno != errno.ENOENT: if exc.errno != errno.ENOENT:
raise raise
# TODO(atash): Figure out which of theses tests is hanging flakily with small # TODO(atash): Figure out which of these tests is hanging flakily with small
# probability. # probability.
def testImportAttributes(self): def testImportAttributes(self):
@ -265,34 +272,33 @@ class PythonPluginTest(unittest.TestCase):
def testUpDown(self): def testUpDown(self):
import test_pb2 import test_pb2
with _CreateService( with _CreateService(
test_pb2, DOES_NOT_MATTER_DELAY) as (servicer, stub, unused_server): test_pb2, NO_DELAY) as (servicer, stub, unused_server):
request = test_pb2.SimpleRequest(response_size=13) request = test_pb2.SimpleRequest(response_size=13)
def testUnaryCall(self): def testUnaryCall(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server):
timeout = 6 # TODO(issue 2039): LONG_TIMEOUT like the other methods.
request = test_pb2.SimpleRequest(response_size=13) request = test_pb2.SimpleRequest(response_size=13)
response = stub.UnaryCall(request, NORMAL_TIMEOUT) response = stub.UnaryCall(request, timeout)
expected_response = methods.UnaryCall(request, 'not a real RpcContext!') expected_response = methods.UnaryCall(request, 'not a real RpcContext!')
self.assertEqual(expected_response, response) self.assertEqual(expected_response, response)
def testUnaryCallAsync(self): def testUnaryCallAsync(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
request = test_pb2.SimpleRequest(response_size=13) request = test_pb2.SimpleRequest(response_size=13)
with _CreateService(test_pb2, LONG_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
start_time = time.clock() # Check that the call does not block waiting for the server to respond.
response_future = stub.UnaryCall.async(request, LONG_TIMEOUT) with methods.pause():
# Check that we didn't block on the asynchronous call. response_future = stub.UnaryCall.async(request, LONG_TIMEOUT)
self.assertGreater(LONG_DELAY, time.clock() - start_time)
response = response_future.result() response = response_future.result()
expected_response = methods.UnaryCall(request, 'not a real RpcContext!') expected_response = methods.UnaryCall(request, 'not a real RpcContext!')
self.assertEqual(expected_response, response) self.assertEqual(expected_response, response)
def testUnaryCallAsyncExpired(self): def testUnaryCallAsyncExpired(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
# set the timeout super low... with _CreateService(test_pb2, NO_DELAY) as (
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
request = test_pb2.SimpleRequest(response_size=13) request = test_pb2.SimpleRequest(response_size=13)
with methods.pause(): with methods.pause():
@ -305,7 +311,7 @@ class PythonPluginTest(unittest.TestCase):
def testUnaryCallAsyncCancelled(self): def testUnaryCallAsyncCancelled(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
request = test_pb2.SimpleRequest(response_size=13) request = test_pb2.SimpleRequest(response_size=13)
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
with methods.pause(): with methods.pause():
response_future = stub.UnaryCall.async(request, 1) response_future = stub.UnaryCall.async(request, 1)
@ -315,17 +321,17 @@ class PythonPluginTest(unittest.TestCase):
def testUnaryCallAsyncFailed(self): def testUnaryCallAsyncFailed(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
request = test_pb2.SimpleRequest(response_size=13) request = test_pb2.SimpleRequest(response_size=13)
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
with methods.fail(): with methods.fail():
response_future = stub.UnaryCall.async(request, NORMAL_TIMEOUT) response_future = stub.UnaryCall.async(request, LONG_TIMEOUT)
self.assertIsNotNone(response_future.exception()) self.assertIsNotNone(response_future.exception())
def testStreamingOutputCall(self): def testStreamingOutputCall(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
request = _streaming_output_request(test_pb2) request = _streaming_output_request(test_pb2)
with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server):
responses = stub.StreamingOutputCall(request, NORMAL_TIMEOUT) responses = stub.StreamingOutputCall(request, LONG_TIMEOUT)
expected_responses = methods.StreamingOutputCall( expected_responses = methods.StreamingOutputCall(
request, 'not a real RpcContext!') request, 'not a real RpcContext!')
for expected_response, response in itertools.izip_longest( for expected_response, response in itertools.izip_longest(
@ -337,7 +343,7 @@ class PythonPluginTest(unittest.TestCase):
def testStreamingOutputCallExpired(self): def testStreamingOutputCallExpired(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
request = _streaming_output_request(test_pb2) request = _streaming_output_request(test_pb2)
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
with methods.pause(): with methods.pause():
responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT) responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT)
@ -349,7 +355,7 @@ class PythonPluginTest(unittest.TestCase):
def testStreamingOutputCallCancelled(self): def testStreamingOutputCallCancelled(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
request = _streaming_output_request(test_pb2) request = _streaming_output_request(test_pb2)
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
unused_methods, stub, unused_server): unused_methods, stub, unused_server):
responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT) responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT)
next(responses) next(responses)
@ -362,7 +368,7 @@ class PythonPluginTest(unittest.TestCase):
def testStreamingOutputCallFailed(self): def testStreamingOutputCallFailed(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
request = _streaming_output_request(test_pb2) request = _streaming_output_request(test_pb2)
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
with methods.fail(): with methods.fail():
responses = stub.StreamingOutputCall(request, 1) responses = stub.StreamingOutputCall(request, 1)
@ -375,20 +381,19 @@ class PythonPluginTest(unittest.TestCase):
def testStreamingInputCall(self): def testStreamingInputCall(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server):
response = stub.StreamingInputCall(StreamingInputRequest(test_pb2), response = stub.StreamingInputCall(
NORMAL_TIMEOUT) _streaming_input_request_iterator(test_pb2), LONG_TIMEOUT)
expected_response = methods.StreamingInputCall( expected_response = methods.StreamingInputCall(
_streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!')
self.assertEqual(expected_response, response) self.assertEqual(expected_response, response)
def testStreamingInputCallAsync(self): def testStreamingInputCallAsync(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
with _CreateService(test_pb2, LONG_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
start_time = time.clock() with methods.pause():
response_future = stub.StreamingInputCall.async( response_future = stub.StreamingInputCall.async(
_streaming_input_request_iterator(test_pb2), LONG_TIMEOUT) _streaming_input_request_iterator(test_pb2), LONG_TIMEOUT)
self.assertGreater(LONG_DELAY, time.clock() - start_time)
response = response_future.result() response = response_future.result()
expected_response = methods.StreamingInputCall( expected_response = methods.StreamingInputCall(
_streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!')
@ -396,8 +401,7 @@ class PythonPluginTest(unittest.TestCase):
def testStreamingInputCallAsyncExpired(self): def testStreamingInputCallAsyncExpired(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
# set the timeout super low... with _CreateService(test_pb2, NO_DELAY) as (
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
with methods.pause(): with methods.pause():
response_future = stub.StreamingInputCall.async( response_future = stub.StreamingInputCall.async(
@ -409,11 +413,12 @@ class PythonPluginTest(unittest.TestCase):
def testStreamingInputCallAsyncCancelled(self): def testStreamingInputCallAsyncCancelled(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
with methods.pause(): with methods.pause():
timeout = 6 # TODO(issue 2039): LONG_TIMEOUT like the other methods.
response_future = stub.StreamingInputCall.async( response_future = stub.StreamingInputCall.async(
_streaming_input_request_iterator(test_pb2), NORMAL_TIMEOUT) _streaming_input_request_iterator(test_pb2), timeout)
response_future.cancel() response_future.cancel()
self.assertTrue(response_future.cancelled()) self.assertTrue(response_future.cancelled())
with self.assertRaises(future.CancelledError): with self.assertRaises(future.CancelledError):
@ -421,7 +426,7 @@ class PythonPluginTest(unittest.TestCase):
def testStreamingInputCallAsyncFailed(self): def testStreamingInputCallAsyncFailed(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
with methods.fail(): with methods.fail():
response_future = stub.StreamingInputCall.async( response_future = stub.StreamingInputCall.async(
@ -432,7 +437,7 @@ class PythonPluginTest(unittest.TestCase):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server):
responses = stub.FullDuplexCall( responses = stub.FullDuplexCall(
_full_duplex_request_iterator(test_pb2), NORMAL_TIMEOUT) _full_duplex_request_iterator(test_pb2), LONG_TIMEOUT)
expected_responses = methods.FullDuplexCall( expected_responses = methods.FullDuplexCall(
_full_duplex_request_iterator(test_pb2), 'not a real RpcContext!') _full_duplex_request_iterator(test_pb2), 'not a real RpcContext!')
for expected_response, response in itertools.izip_longest( for expected_response, response in itertools.izip_longest(
@ -444,7 +449,7 @@ class PythonPluginTest(unittest.TestCase):
def testFullDuplexCallExpired(self): def testFullDuplexCallExpired(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
request_iterator = _full_duplex_request_iterator(test_pb2) request_iterator = _full_duplex_request_iterator(test_pb2)
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
with methods.pause(): with methods.pause():
responses = stub.FullDuplexCall(request_iterator, SHORT_TIMEOUT) responses = stub.FullDuplexCall(request_iterator, SHORT_TIMEOUT)
@ -457,7 +462,7 @@ class PythonPluginTest(unittest.TestCase):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server):
request_iterator = _full_duplex_request_iterator(test_pb2) request_iterator = _full_duplex_request_iterator(test_pb2)
responses = stub.FullDuplexCall(request_iterator, NORMAL_TIMEOUT) responses = stub.FullDuplexCall(request_iterator, LONG_TIMEOUT)
next(responses) next(responses)
responses.cancel() responses.cancel()
with self.assertRaises(future.CancelledError): with self.assertRaises(future.CancelledError):
@ -468,10 +473,10 @@ class PythonPluginTest(unittest.TestCase):
def testFullDuplexCallFailed(self): def testFullDuplexCallFailed(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
request_iterator = _full_duplex_request_iterator(test_pb2) request_iterator = _full_duplex_request_iterator(test_pb2)
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
with methods.fail(): with methods.fail():
responses = stub.FullDuplexCall(request_iterator, NORMAL_TIMEOUT) responses = stub.FullDuplexCall(request_iterator, LONG_TIMEOUT)
self.assertIsNotNone(responses) self.assertIsNotNone(responses)
with self.assertRaises(exceptions.ServicerError): with self.assertRaises(exceptions.ServicerError):
next(responses) next(responses)
@ -480,7 +485,7 @@ class PythonPluginTest(unittest.TestCase):
'forever and fix.') 'forever and fix.')
def testHalfDuplexCall(self): def testHalfDuplexCall(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
with _CreateService(test_pb2, DOES_NOT_MATTER_DELAY) as ( with _CreateService(test_pb2, NO_DELAY) as (
methods, stub, unused_server): methods, stub, unused_server):
def half_duplex_request_iterator(): def half_duplex_request_iterator():
request = test_pb2.StreamingOutputCallRequest() request = test_pb2.StreamingOutputCallRequest()
@ -491,32 +496,37 @@ class PythonPluginTest(unittest.TestCase):
request.response_parameters.add(size=3, interval_us=0) request.response_parameters.add(size=3, interval_us=0)
yield request yield request
responses = stub.HalfDuplexCall( responses = stub.HalfDuplexCall(
half_duplex_request_iterator(), NORMAL_TIMEOUT) half_duplex_request_iterator(), LONG_TIMEOUT)
expected_responses = methods.HalfDuplexCall( expected_responses = methods.HalfDuplexCall(
HalfDuplexRequest(), 'not a real RpcContext!') half_duplex_request_iterator(), 'not a real RpcContext!')
for check in itertools.izip_longest(expected_responses, responses): for check in itertools.izip_longest(expected_responses, responses):
expected_response, response = check expected_response, response = check
self.assertEqual(expected_response, response) self.assertEqual(expected_response, response)
def testHalfDuplexCallWedged(self): def testHalfDuplexCallWedged(self):
import test_pb2 # pylint: disable=g-import-not-at-top import test_pb2 # pylint: disable=g-import-not-at-top
condition = threading.Condition()
wait_cell = [False] wait_cell = [False]
@contextlib.contextmanager @contextlib.contextmanager
def wait(): # pylint: disable=invalid-name def wait(): # pylint: disable=invalid-name
# Where's Python 3's 'nonlocal' statement when you need it? # Where's Python 3's 'nonlocal' statement when you need it?
wait_cell[0] = True with condition:
wait_cell[0] = True
yield yield
wait_cell[0] = False with condition:
wait_cell[0] = False
condition.notify_all()
def half_duplex_request_iterator(): def half_duplex_request_iterator():
request = test_pb2.StreamingOutputCallRequest() request = test_pb2.StreamingOutputCallRequest()
request.response_parameters.add(size=1, interval_us=0) request.response_parameters.add(size=1, interval_us=0)
yield request yield request
while wait_cell[0]: with condition:
time.sleep(0.1) while wait_cell[0]:
condition.wait()
with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server):
with wait(): with wait():
responses = stub.HalfDuplexCall( responses = stub.HalfDuplexCall(
half_duplex_request_iterator(), NORMAL_TIMEOUT) half_duplex_request_iterator(), SHORT_TIMEOUT)
# half-duplex waits for the client to send all info # half-duplex waits for the client to send all info
with self.assertRaises(exceptions.ExpirationError): with self.assertRaises(exceptions.ExpirationError):
next(responses) next(responses)

@ -38,5 +38,5 @@ rm -rf python2.7_virtual_environment
virtualenv -p /usr/bin/python2.7 python2.7_virtual_environment virtualenv -p /usr/bin/python2.7 python2.7_virtual_environment
source python2.7_virtual_environment/bin/activate source python2.7_virtual_environment/bin/activate
pip install -r src/python/requirements.txt pip install -r src/python/requirements.txt
CFLAGS=-I$root/include LDFLAGS=-L$root/libs/$CONFIG pip install src/python/src CFLAGS="-I$root/include -std=c89" LDFLAGS=-L$root/libs/$CONFIG pip install src/python/src
pip install src/python/interop pip install src/python/interop

@ -223,6 +223,7 @@ class Jobset(object):
self._travis = travis self._travis = travis
self._cache = cache self._cache = cache
self._stop_on_failure = stop_on_failure self._stop_on_failure = stop_on_failure
self._hashes = {}
def start(self, spec): def start(self, spec):
"""Start a job. Return True on success, False on failure.""" """Start a job. Return True on success, False on failure."""
@ -231,11 +232,15 @@ class Jobset(object):
self.reap() self.reap()
if self.cancelled(): return False if self.cancelled(): return False
if spec.hash_targets: if spec.hash_targets:
bin_hash = hashlib.sha1() if spec.identity() in self._hashes:
for fn in spec.hash_targets: bin_hash = self._hashes[spec.identity()]
with open(which(fn)) as f: else:
bin_hash.update(f.read()) bin_hash = hashlib.sha1()
bin_hash = bin_hash.hexdigest() for fn in spec.hash_targets:
with open(which(fn)) as f:
bin_hash.update(f.read())
bin_hash = bin_hash.hexdigest()
self._hashes[spec.identity()] = bin_hash
should_run = self._cache.should_run(spec.identity(), bin_hash) should_run = self._cache.should_run(spec.identity(), bin_hash)
else: else:
bin_hash = None bin_hash = None
@ -266,6 +271,7 @@ class Jobset(object):
for job in self._running: for job in self._running:
job.kill() job.kill()
dead.add(job) dead.add(job)
break
for job in dead: for job in dead:
self._completed += 1 self._completed += 1
self._running.remove(job) self._running.remove(job)

@ -313,7 +313,7 @@ _CONFIGS = {
'dbg': SimpleConfig('dbg'), 'dbg': SimpleConfig('dbg'),
'opt': SimpleConfig('opt'), 'opt': SimpleConfig('opt'),
'tsan': SimpleConfig('tsan', environ={ 'tsan': SimpleConfig('tsan', environ={
'TSAN_OPTIONS': 'suppressions=tools/tsan_suppressions.txt'}), 'TSAN_OPTIONS': 'suppressions=tools/tsan_suppressions.txt:halt_on_error=1'}),
'msan': SimpleConfig('msan'), 'msan': SimpleConfig('msan'),
'ubsan': SimpleConfig('ubsan'), 'ubsan': SimpleConfig('ubsan'),
'asan': SimpleConfig('asan', environ={ 'asan': SimpleConfig('asan', environ={
@ -449,6 +449,7 @@ class TestCache(object):
def __init__(self, use_cache_results): def __init__(self, use_cache_results):
self._last_successful_run = {} self._last_successful_run = {}
self._use_cache_results = use_cache_results self._use_cache_results = use_cache_results
self._last_save = time.time()
def should_run(self, cmdline, bin_hash): def should_run(self, cmdline, bin_hash):
if cmdline not in self._last_successful_run: if cmdline not in self._last_successful_run:
@ -461,7 +462,8 @@ class TestCache(object):
def finished(self, cmdline, bin_hash): def finished(self, cmdline, bin_hash):
self._last_successful_run[cmdline] = bin_hash self._last_successful_run[cmdline] = bin_hash
self.save() if time.time() - self._last_save > 1:
self.save()
def dump(self): def dump(self):
return [{'cmdline': k, 'hash': v} return [{'cmdline': k, 'hash': v}
@ -473,6 +475,7 @@ class TestCache(object):
def save(self): def save(self):
with open('.run_tests_cache', 'w') as f: with open('.run_tests_cache', 'w') as f:
f.write(json.dumps(self.dump())) f.write(json.dumps(self.dump()))
self._last_save = time.time()
def maybe_load(self): def maybe_load(self):
if os.path.exists('.run_tests_cache'): if os.path.exists('.run_tests_cache'):
@ -515,6 +518,8 @@ def _build_and_run(check_cancelled, newline_on_success, travis, cache):
for antagonist in antagonists: for antagonist in antagonists:
antagonist.kill() antagonist.kill()
if cache: cache.save()
return 0 return 0

Loading…
Cancel
Save