Merge remote-tracking branch 'upstream/master'

pull/19826/head
Karthik Ravi Shankar 6 years ago
commit ad1e05214b
  1. 7
      WORKSPACE
  2. 215
      bazel/generate_objc.bzl
  3. 50
      bazel/grpc_build_system.bzl
  4. 8
      bazel/grpc_deps.bzl
  5. 46
      bazel/grpc_util.bzl
  6. 68
      bazel/objc_grpc_library.bzl
  7. 2
      src/core/ext/filters/client_channel/client_channel.cc
  8. 96
      src/objective-c/BUILD
  9. 69
      src/objective-c/examples/BUILD
  10. 31
      src/objective-c/examples/BazelBuildSamples/ios-sample/Podfile
  11. 413
      src/objective-c/examples/BazelBuildSamples/ios-sample/ios-sample.xcodeproj/project.pbxproj
  12. 25
      src/objective-c/examples/BazelBuildSamples/ios-sample/ios-sample/AppDelegate.h
  13. 63
      src/objective-c/examples/BazelBuildSamples/ios-sample/ios-sample/AppDelegate.m
  14. 98
      src/objective-c/examples/BazelBuildSamples/ios-sample/ios-sample/Assets.xcassets/AppIcon.appiconset/Contents.json
  15. 6
      src/objective-c/examples/BazelBuildSamples/ios-sample/ios-sample/Assets.xcassets/Contents.json
  16. 25
      src/objective-c/examples/BazelBuildSamples/ios-sample/ios-sample/Base.lproj/LaunchScreen.storyboard
  17. 38
      src/objective-c/examples/BazelBuildSamples/ios-sample/ios-sample/Base.lproj/Main.storyboard
  18. 45
      src/objective-c/examples/BazelBuildSamples/ios-sample/ios-sample/Info.plist
  19. 23
      src/objective-c/examples/BazelBuildSamples/ios-sample/ios-sample/ViewController.h
  20. 86
      src/objective-c/examples/BazelBuildSamples/ios-sample/ios-sample/ViewController.m
  21. 26
      src/objective-c/examples/BazelBuildSamples/ios-sample/ios-sample/main.m
  22. 118
      src/objective-c/examples/BazelBuildSamples/messages.proto
  23. 28
      src/objective-c/examples/BazelBuildSamples/rmt/BUILD
  24. 57
      src/objective-c/examples/BazelBuildSamples/rmt/test.proto
  25. 151
      src/python/grpcio/grpc/_channel.py
  26. 50
      src/python/grpcio/grpc/_common.py
  27. 2
      src/python/grpcio_tests/commands.py
  28. 1
      src/python/grpcio_tests/tests/tests.json
  29. 8
      src/python/grpcio_tests/tests/unit/BUILD.bazel
  30. 84
      src/python/grpcio_tests/tests/unit/_signal_client.py
  31. 172
      src/python/grpcio_tests/tests/unit/_signal_handling_test.py
  32. 11
      tools/run_tests/sanity/check_bazel_workspace.py

@ -76,3 +76,10 @@ api_dependencies()
load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains")
go_rules_dependencies()
go_register_toolchains()
load("@build_bazel_rules_apple//apple:repositories.bzl", "apple_rules_dependencies")
apple_rules_dependencies()
load("@build_bazel_apple_support//lib:repositories.bzl", "apple_support_dependencies")
apple_support_dependencies()

@ -0,0 +1,215 @@
load(
"//bazel:protobuf.bzl",
"get_include_protoc_args",
"get_plugin_args",
"proto_path_to_generated_filename",
)
load(":grpc_util.bzl", "to_upper_camel_with_extension",)
_GRPC_PROTO_HEADER_FMT = "{}.pbrpc.h"
_GRPC_PROTO_SRC_FMT = "{}.pbrpc.m"
_PROTO_HEADER_FMT = "{}.pbobjc.h"
_PROTO_SRC_FMT = "{}.pbobjc.m"
_GENERATED_PROTOS_DIR = "_generated_protos"
_GENERATE_HDRS = 1
_GENERATE_SRCS = 2
_GENERATE_NON_ARC_SRCS = 3
def _generate_objc_impl(ctx):
"""Implementation of the generate_objc rule."""
protos = [
f
for src in ctx.attr.deps
for f in src[ProtoInfo].transitive_imports.to_list()
]
target_package = _join_directories([ctx.label.workspace_root, ctx.label.package])
files_with_rpc = [_label_to_full_file_path(f, target_package) for f in ctx.attr.srcs]
outs = []
for proto in protos:
outs += [_get_output_file_name_from_proto(proto, _PROTO_HEADER_FMT)]
outs += [_get_output_file_name_from_proto(proto, _PROTO_SRC_FMT)]
file_path = _get_full_path_from_file(proto)
if file_path in files_with_rpc:
outs += [_get_output_file_name_from_proto(proto, _GRPC_PROTO_HEADER_FMT)]
outs += [_get_output_file_name_from_proto(proto, _GRPC_PROTO_SRC_FMT)]
out_files = [ctx.actions.declare_file(out) for out in outs]
dir_out = _join_directories([
str(ctx.genfiles_dir.path), target_package, _GENERATED_PROTOS_DIR
])
arguments = []
if ctx.executable.plugin:
arguments += get_plugin_args(
ctx.executable.plugin,
[],
dir_out,
False,
)
tools = [ctx.executable.plugin]
arguments += ["--objc_out=" + dir_out]
arguments += ["--proto_path=."]
arguments += get_include_protoc_args(protos)
# Include the output directory so that protoc puts the generated code in the
# right directory.
arguments += ["--proto_path={}".format(dir_out)]
arguments += ["--proto_path={}".format(_get_directory_from_proto(proto)) for proto in protos]
arguments += [_get_full_path_from_file(proto) for proto in protos]
# create a list of well known proto files if the argument is non-None
well_known_proto_files = []
if ctx.attr.use_well_known_protos:
f = ctx.attr.well_known_protos.files.to_list()[0].dirname
# go two levels up so that #import "google/protobuf/..." is correct
arguments += ["-I{0}".format(f + "/../..")]
well_known_proto_files = ctx.attr.well_known_protos.files.to_list()
ctx.actions.run(
inputs = protos + well_known_proto_files,
tools = tools,
outputs = out_files,
executable = ctx.executable._protoc,
arguments = arguments,
)
return struct(files = depset(out_files))
def _label_to_full_file_path(src, package):
if not src.startswith("//"):
# Relative from current package
if not src.startswith(":"):
# "a.proto" -> ":a.proto"
src = ":" + src
src = "//" + package + src
# Converts //path/to/package:File.ext to path/to/package/File.ext.
src = src.replace("//", "")
src = src.replace(":", "/")
if src.startswith("/"):
# "//:a.proto" -> "/a.proto" so remove the initial slash
return src[1:]
else:
return src
def _get_output_file_name_from_proto(proto, fmt):
return proto_path_to_generated_filename(
_GENERATED_PROTOS_DIR + "/" +
_get_directory_from_proto(proto) + _get_slash_or_null_from_proto(proto) +
to_upper_camel_with_extension(_get_file_name_from_proto(proto), "proto"),
fmt,
)
def _get_file_name_from_proto(proto):
return proto.path.rpartition("/")[2]
def _get_slash_or_null_from_proto(proto):
"""Potentially returns empty (if the file is in the root directory)"""
return proto.path.rpartition("/")[1]
def _get_directory_from_proto(proto):
return proto.path.rpartition("/")[0]
def _get_full_path_from_file(file):
gen_dir_length = 0
# if file is generated, then prepare to remote its root
# (including CPU architecture...)
if not file.is_source:
gen_dir_length = len(file.root.path) + 1
return file.path[gen_dir_length:]
def _join_directories(directories):
massaged_directories = [directory for directory in directories if len(directory) != 0]
return "/".join(massaged_directories)
generate_objc = rule(
attrs = {
"deps": attr.label_list(
mandatory = True,
allow_empty = False,
providers = [ProtoInfo],
),
"plugin": attr.label(
default = "@com_github_grpc_grpc//src/compiler:grpc_objective_c_plugin",
executable = True,
providers = ["files_to_run"],
cfg = "host",
),
"srcs": attr.string_list(
mandatory = False,
allow_empty = True
),
"use_well_known_protos": attr.bool(
mandatory = False,
default = False
),
"well_known_protos": attr.label(
default = "@com_google_protobuf//:well_known_protos"
),
"_protoc": attr.label(
default = Label("//external:protocol_compiler"),
executable = True,
cfg = "host",
),
},
output_to_genfiles = True,
implementation = _generate_objc_impl
)
def _group_objc_files_impl(ctx):
suffix = ""
if ctx.attr.gen_mode == _GENERATE_HDRS:
suffix = "h"
elif ctx.attr.gen_mode == _GENERATE_SRCS:
suffix = "pbrpc.m"
elif ctx.attr.gen_mode == _GENERATE_NON_ARC_SRCS:
suffix = "pbobjc.m"
else:
fail("Undefined gen_mode")
out_files = [
file
for file in ctx.attr.src.files.to_list()
if file.basename.endswith(suffix)
]
return struct(files = depset(out_files))
generate_objc_hdrs = rule(
attrs = {
"src": attr.label(
mandatory = True,
),
"gen_mode": attr.int(
default = _GENERATE_HDRS,
)
},
implementation = _group_objc_files_impl
)
generate_objc_srcs = rule(
attrs = {
"src": attr.label(
mandatory = True,
),
"gen_mode": attr.int(
default = _GENERATE_SRCS,
)
},
implementation = _group_objc_files_impl
)
generate_objc_non_arc_srcs = rule(
attrs = {
"src": attr.label(
mandatory = True,
),
"gen_mode": attr.int(
default = _GENERATE_NON_ARC_SRCS,
)
},
implementation = _group_objc_files_impl
)

@ -24,6 +24,7 @@
#
load("//bazel:cc_grpc_library.bzl", "cc_grpc_library")
load("@build_bazel_rules_apple//apple:resources.bzl", "apple_resource_bundle")
load("@upb//bazel:upb_proto_library.bzl", "upb_proto_library")
# The set of pollers to test against if a test exercises polling
@ -198,6 +199,19 @@ def grpc_cc_binary(name, srcs = [], deps = [], external_deps = [], args = [], da
)
def grpc_generate_one_off_targets():
apple_resource_bundle(
# The choice of name is signicant here, since it determines the bundle name.
name = "gRPCCertificates",
resources = ["etc/roots.pem"],
)
# In open-source, grpc_objc* libraries depend directly on //:grpc
native.alias(
name = "grpc_objc",
actual = "//:grpc",
)
def grpc_objc_use_cronet_config():
pass
def grpc_sh_test(name, srcs, args = [], data = []):
@ -240,6 +254,42 @@ def grpc_package(name, visibility = "private", features = []):
features = features,
)
def grpc_objc_library(
name,
srcs,
hdrs = [],
textual_hdrs = [],
data = [],
deps = [],
defines = [],
includes = [],
visibility = ["//visibility:public"]):
"""The grpc version of objc_library, only used for the Objective-C library compilation
Args:
name: name of target
hdrs: public headers
srcs: all source files (.m)
textual_hdrs: private headers
data: any other bundle resources
defines: preprocessors
includes: added to search path, always [the path to objc directory]
deps: dependencies
visibility: visibility, default to public
"""
native.objc_library(
name = name,
hdrs = hdrs,
srcs = srcs,
textual_hdrs = textual_hdrs,
data = data,
deps = deps,
defines = defines,
includes = includes,
visibility = visibility,
)
def grpc_upb_proto_library(name, deps):
upb_proto_library(name = name, deps = deps)

@ -224,6 +224,14 @@ def grpc_deps():
urls = ["https://github.com/bazelbuild/rules_go/releases/download/0.18.5/rules_go-0.18.5.tar.gz"],
sha256 = "a82a352bffae6bee4e95f68a8d80a70e87f42c4741e6a448bec11998fcc82329",
)
if "build_bazel_rules_apple" not in native.existing_rules():
git_repository(
name = "build_bazel_rules_apple",
remote = "https://github.com/bazelbuild/rules_apple.git",
tag = "0.17.2",
)
# TODO: move some dependencies from "grpc_deps" here?
def grpc_test_only_deps():
"""Internal, not intended for use by packages that are consuming grpc.

@ -0,0 +1,46 @@
# Follows convention set in objectivec_helpers.cc in the protobuf ObjC compiler.
_upper_segments_list = ["url", "http", "https"]
def strip_extension(str):
return str.rpartition(".")[0]
def capitalize(word):
if word in _upper_segments_list:
return word.upper()
else:
return word.capitalize()
def lower_underscore_to_upper_camel(str):
str = strip_extension(str)
camel_case_str = ""
word = ""
for c in str.elems(): # NB: assumes ASCII!
if c.isalpha():
word += c.lower()
else:
# Last word is finished.
if len(word):
camel_case_str += capitalize(word)
word = ""
if c.isdigit():
camel_case_str += c
# Otherwise, drop the character. See UnderscoresToCamelCase in:
# third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
if len(word):
camel_case_str += capitalize(word)
return camel_case_str
def file_to_upper_camel(src):
elements = src.rpartition("/")
upper_camel = lower_underscore_to_upper_camel(elements[-1])
return "".join(list(elements[:-1]) + [upper_camel])
def file_with_extension(src, ext):
elements = src.rpartition("/")
return "".join(list(elements[:-1]) + [elements[-1], "." + ext])
def to_upper_camel_with_extension(src, ext):
src = file_to_upper_camel(src)
return file_with_extension(src, ext)

@ -0,0 +1,68 @@
load(
"//bazel:generate_objc.bzl",
"generate_objc",
"generate_objc_hdrs",
"generate_objc_srcs",
"generate_objc_non_arc_srcs"
)
load("//bazel:protobuf.bzl", "well_known_proto_libs")
def objc_grpc_library(name, deps, srcs = [], use_well_known_protos = False, **kwargs):
"""Generates messages and/or service stubs for given proto_library and all transitively dependent proto files
Args:
name: name of target
deps: a list of proto_library targets that needs to be compiled
srcs: a list of labels to proto files with service stubs to be generated,
labels specified must include service stubs; otherwise Bazel will complain about srcs being empty
use_well_known_protos: whether to use the well known protos defined in
@com_google_protobuf//src/google/protobuf, default to false
**kwargs: other arguments
"""
objc_grpc_library_name = "_" + name + "_objc_grpc_library"
generate_objc(
name = objc_grpc_library_name,
srcs = srcs,
deps = deps,
use_well_known_protos = use_well_known_protos,
**kwargs
)
generate_objc_hdrs(
name = objc_grpc_library_name + "_hdrs",
src = ":" + objc_grpc_library_name,
)
generate_objc_non_arc_srcs(
name = objc_grpc_library_name + "_non_arc_srcs",
src = ":" + objc_grpc_library_name,
)
arc_srcs = None
if len(srcs) > 0:
generate_objc_srcs(
name = objc_grpc_library_name + "_srcs",
src = ":" + objc_grpc_library_name,
)
arc_srcs = [":" + objc_grpc_library_name + "_srcs"]
native.objc_library(
name = name,
hdrs = [":" + objc_grpc_library_name + "_hdrs"],
non_arc_srcs = [":" + objc_grpc_library_name + "_non_arc_srcs"],
srcs = arc_srcs,
defines = [
"GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=0",
"GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO=0",
],
includes = [
"_generated_protos",
"src/objective-c",
],
deps = [
"@com_github_grpc_grpc//src/objective-c:proto_objc_rpc",
"@com_google_protobuf//:protobuf_objc",
],
)

@ -1510,7 +1510,7 @@ void ChannelData::CreateResolvingLoadBalancingPolicyLocked() {
UniquePtr<LoadBalancingPolicy::ChannelControlHelper>(
New<ClientChannelControlHelper>(this));
lb_args.args = channel_args_;
UniquePtr<char> target_uri(strdup(target_uri_.get()));
UniquePtr<char> target_uri(gpr_strdup(target_uri_.get()));
resolving_lb_policy_.reset(New<ResolvingLoadBalancingPolicy>(
std::move(lb_args), &grpc_client_channel_routing_trace,
std::move(target_uri), ProcessResolverResultLocked, this));

@ -0,0 +1,96 @@
# gRPC Bazel BUILD file.
#
# Copyright 2019 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
licenses(["notice"]) # Apache v2
package(default_visibility = ["//visibility:public"])
load("//bazel:grpc_build_system.bzl", "grpc_objc_library", "grpc_objc_use_cronet_config")
exports_files(["LICENSE"])
grpc_objc_use_cronet_config()
grpc_objc_library(
name = "rx_library",
srcs = glob([
"RxLibrary/*.m",
"RxLibrary/transformations/*.m",
]),
hdrs = glob([
"RxLibrary/*.h",
"RxLibrary/transformations/*.h",
]),
includes = ["."],
deps = [":rx_library_private"],
)
grpc_objc_library(
name = "rx_library_private",
srcs = glob([
"RxLibrary/private/*.m",
]),
textual_hdrs = glob([
"RxLibrary/private/*.h",
]),
visibility = ["//visibility:private"],
)
grpc_objc_library(
name = "grpc_objc_client",
srcs = glob(
[
"GRPCClient/*.m",
"GRPCClient/private/*.m",
],
exclude = ["GRPCClient/GRPCCall+GID.m"],
),
hdrs = glob(
[
"GRPCClient/*.h",
"GRPCClient/internal/*.h",
],
exclude = ["GRPCClient/GRPCCall+GID.h"],
),
data = ["//:gRPCCertificates"],
includes = ["."],
textual_hdrs = glob([
"GRPCClient/private/*.h",
]),
deps = [
":rx_library",
"//:grpc_objc",
],
)
grpc_objc_library(
name = "proto_objc_rpc",
srcs = glob(
["ProtoRPC/*.m"],
),
hdrs = glob(
["ProtoRPC/*.h"],
),
# Different from Cocoapods, do not import as if @com_google_protobuf//:protobuf_objc is a framework,
# use the real paths of @com_google_protobuf//:protobuf_objc instead
defines = ["GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=0"],
includes = ["src/objective-c"],
deps = [
":grpc_objc_client",
":rx_library",
"@com_google_protobuf//:protobuf_objc",
],
)

@ -0,0 +1,69 @@
# gRPC Bazel BUILD file.
#
# Copyright 2019 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application")
load(
"@com_github_grpc_grpc//bazel:objc_grpc_library.bzl",
"objc_grpc_library",
)
proto_library(
name = "messages_proto",
srcs = ["BazelBuildSamples/messages.proto"],
visibility = ["//visibility:public"],
)
objc_grpc_library(
name = "test_grpc_objc",
srcs = ["BazelBuildSamples/rmt/test.proto"],
use_well_known_protos = True,
deps = [
"//src/objective-c/examples/BazelBuildSamples/rmt:test_proto",
],
)
# Proof that without this works without srcs
objc_grpc_library(
name = "test_objc",
use_well_known_protos = True,
deps = [
"//src/objective-c/examples/BazelBuildSamples/rmt:test_proto",
]
)
objc_library(
name = "ios-sample-lib",
srcs = glob(["BazelBuildSamples/ios-sample/ios-sample/**/*.m"]),
hdrs = glob(["BazelBuildSamples/ios-sample/ios-sample/**/*.h"]),
data = glob([
"BazelBuildSamples/ios-sample/ios-sample/Assets.xcassets/**/*",
"BazelBuildSamples/ios-sample/ios-sample/Base.lproj/**/*"
]),
deps = [
":test_grpc_objc",
]
)
ios_application(
name = "ios-sample",
bundle_id = "com.google.ios-sample-objc-bazel",
families = ["iphone"],
minimum_os_version = "9.0",
infoplists = ["BazelBuildSamples/ios-sample/ios-sample/Info.plist"],
visibility = ["//visibility:public"],
deps = [":ios-sample-lib"],
)

@ -0,0 +1,31 @@
platform :ios, '8.0'
install! 'cocoapods', :deterministic_uuids => false
ROOT_DIR = '../../../../..'
target 'ios-sample' do
pod 'gRPC-ProtoRPC', :path => ROOT_DIR
pod 'gRPC', :path => ROOT_DIR
pod 'gRPC-Core', :path => ROOT_DIR
pod 'gRPC-RxLibrary', :path => ROOT_DIR
pod 'RemoteTest', :path => "../../RemoteTestClient"
pod '!ProtoCompiler-gRPCPlugin', :path => "#{ROOT_DIR}/src/objective-c"
end
pre_install do |installer|
grpc_core_spec = installer.pod_targets.find{|t| t.name.start_with?('gRPC-Core')}.root_spec
src_root = "$(PODS_TARGET_SRCROOT)"
grpc_core_spec.pod_target_xcconfig = {
'GRPC_SRC_ROOT' => src_root,
'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"',
'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"',
# If we don't set these two settings, `include/grpc/support/time.h` and
# `src/core/lib/gpr/string.h` shadow the system `<time.h>` and `<string.h>`, breaking the
# build.
'USE_HEADERMAP' => 'NO',
'ALWAYS_SEARCH_USER_PATHS' => 'NO',
}
end

@ -0,0 +1,413 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
AB433CC922D7E38000D579CC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AB433CC822D7E38000D579CC /* AppDelegate.m */; };
AB433CCC22D7E38000D579CC /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AB433CCB22D7E38000D579CC /* ViewController.m */; };
AB433CCF22D7E38000D579CC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AB433CCD22D7E38000D579CC /* Main.storyboard */; };
AB433CD122D7E38100D579CC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AB433CD022D7E38100D579CC /* Assets.xcassets */; };
AB433CD422D7E38100D579CC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AB433CD222D7E38100D579CC /* LaunchScreen.storyboard */; };
AB433CD722D7E38100D579CC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AB433CD622D7E38100D579CC /* main.m */; };
ED11F6CDF54788FC7CFD87B1 /* libPods-ios-sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5AF80A181E30BD84FA56BE33 /* libPods-ios-sample.a */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
112D4595FA3E81552DA9E877 /* Pods-ios-sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-sample.release.xcconfig"; path = "Target Support Files/Pods-ios-sample/Pods-ios-sample.release.xcconfig"; sourceTree = "<group>"; };
5AF80A181E30BD84FA56BE33 /* libPods-ios-sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ios-sample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
72599BE4AC5785D3368D40DD /* Pods-ios-sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-sample.debug.xcconfig"; path = "Target Support Files/Pods-ios-sample/Pods-ios-sample.debug.xcconfig"; sourceTree = "<group>"; };
AB433CC422D7E38000D579CC /* ios-sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ios-sample.app"; sourceTree = BUILT_PRODUCTS_DIR; };
AB433CC722D7E38000D579CC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
AB433CC822D7E38000D579CC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
AB433CCA22D7E38000D579CC /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
AB433CCB22D7E38000D579CC /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
AB433CCE22D7E38000D579CC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
AB433CD022D7E38100D579CC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
AB433CD322D7E38100D579CC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
AB433CD522D7E38100D579CC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AB433CD622D7E38100D579CC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
AB433CC122D7E38000D579CC /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
ED11F6CDF54788FC7CFD87B1 /* libPods-ios-sample.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2A170580B60E92B1A65525D4 /* Pods */ = {
isa = PBXGroup;
children = (
72599BE4AC5785D3368D40DD /* Pods-ios-sample.debug.xcconfig */,
112D4595FA3E81552DA9E877 /* Pods-ios-sample.release.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
AB433CBB22D7E38000D579CC = {
isa = PBXGroup;
children = (
AB433CC622D7E38000D579CC /* ios-sample */,
AB433CC522D7E38000D579CC /* Products */,
2A170580B60E92B1A65525D4 /* Pods */,
FD148AE940967C50DB2C12CB /* Frameworks */,
);
sourceTree = "<group>";
};
AB433CC522D7E38000D579CC /* Products */ = {
isa = PBXGroup;
children = (
AB433CC422D7E38000D579CC /* ios-sample.app */,
);
name = Products;
sourceTree = "<group>";
};
AB433CC622D7E38000D579CC /* ios-sample */ = {
isa = PBXGroup;
children = (
AB433CC722D7E38000D579CC /* AppDelegate.h */,
AB433CC822D7E38000D579CC /* AppDelegate.m */,
AB433CCA22D7E38000D579CC /* ViewController.h */,
AB433CCB22D7E38000D579CC /* ViewController.m */,
AB433CCD22D7E38000D579CC /* Main.storyboard */,
AB433CD022D7E38100D579CC /* Assets.xcassets */,
AB433CD222D7E38100D579CC /* LaunchScreen.storyboard */,
AB433CD522D7E38100D579CC /* Info.plist */,
AB433CD622D7E38100D579CC /* main.m */,
);
path = "ios-sample";
sourceTree = "<group>";
};
FD148AE940967C50DB2C12CB /* Frameworks */ = {
isa = PBXGroup;
children = (
5AF80A181E30BD84FA56BE33 /* libPods-ios-sample.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
AB433CC322D7E38000D579CC /* ios-sample */ = {
isa = PBXNativeTarget;
buildConfigurationList = AB433CDA22D7E38100D579CC /* Build configuration list for PBXNativeTarget "ios-sample" */;
buildPhases = (
9DD34A50D448CD3F464D4A3C /* [CP] Check Pods Manifest.lock */,
AB433CC022D7E38000D579CC /* Sources */,
AB433CC122D7E38000D579CC /* Frameworks */,
AB433CC222D7E38000D579CC /* Resources */,
630985F7228D41528084692C /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = "ios-sample";
productName = "ios-sample";
productReference = AB433CC422D7E38000D579CC /* ios-sample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
AB433CBC22D7E38000D579CC /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1010;
ORGANIZATIONNAME = "Tony Lu";
TargetAttributes = {
AB433CC322D7E38000D579CC = {
CreatedOnToolsVersion = 10.1;
};
};
};
buildConfigurationList = AB433CBF22D7E38000D579CC /* Build configuration list for PBXProject "ios-sample" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = AB433CBB22D7E38000D579CC;
productRefGroup = AB433CC522D7E38000D579CC /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
AB433CC322D7E38000D579CC /* ios-sample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
AB433CC222D7E38000D579CC /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AB433CD422D7E38100D579CC /* LaunchScreen.storyboard in Resources */,
AB433CD122D7E38100D579CC /* Assets.xcassets in Resources */,
AB433CCF22D7E38000D579CC /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
630985F7228D41528084692C /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ios-sample/Pods-ios-sample-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ios-sample/Pods-ios-sample-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ios-sample/Pods-ios-sample-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9DD34A50D448CD3F464D4A3C /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-ios-sample-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
AB433CC022D7E38000D579CC /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AB433CCC22D7E38000D579CC /* ViewController.m in Sources */,
AB433CD722D7E38100D579CC /* main.m in Sources */,
AB433CC922D7E38000D579CC /* AppDelegate.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
AB433CCD22D7E38000D579CC /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
AB433CCE22D7E38000D579CC /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
AB433CD222D7E38100D579CC /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
AB433CD322D7E38100D579CC /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
AB433CD822D7E38100D579CC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
AB433CD922D7E38100D579CC /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
AB433CDB22D7E38100D579CC /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 72599BE4AC5785D3368D40DD /* Pods-ios-sample.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 6T98ZJNPG5;
INFOPLIST_FILE = "ios-sample/Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.google.ios-sample-objc-bazel";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
AB433CDC22D7E38100D579CC /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 112D4595FA3E81552DA9E877 /* Pods-ios-sample.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 6T98ZJNPG5;
INFOPLIST_FILE = "ios-sample/Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.google.ios-sample-objc-bazel";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
AB433CBF22D7E38000D579CC /* Build configuration list for PBXProject "ios-sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
AB433CD822D7E38100D579CC /* Debug */,
AB433CD922D7E38100D579CC /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
AB433CDA22D7E38100D579CC /* Build configuration list for PBXNativeTarget "ios-sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
AB433CDB22D7E38100D579CC /* Debug */,
AB433CDC22D7E38100D579CC /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = AB433CBC22D7E38000D579CC /* Project object */;
}

@ -0,0 +1,25 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder<UIApplicationDelegate>
@property(strong, nonatomic) UIWindow* window;
@end

@ -0,0 +1,63 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for
// certain types of temporary interruptions (such as an incoming phone call or SMS message) or
// when the user quits the application and it begins the transition to the background state. Use
// this method to pause ongoing tasks, disable timers, and invalidate graphics rendering
// callbacks. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store
// enough application state information to restore your application to its current state in case
// it is terminated later. If your application supports background execution, this method is
// called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo
// many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If
// the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also
// applicationDidEnterBackground:.
}
@end

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iTL-zv-YSZ">
<rect key="frame" x="164" y="318" width="46" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" title="Button"/>
<connections>
<action selector="tapCall:" destination="BYZ-38-t0r" eventType="touchUpInside" id="c5c-m5-SGC"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en_US</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

@ -0,0 +1,23 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end

@ -0,0 +1,86 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#import "ViewController.h"
#import <GRPCClient/GRPCCall.h>
#if COCOAPODS
#import <RemoteTest/Messages.pbobjc.h>
#import <RemoteTest/Test.pbrpc.h>
#else
#import "src/objective-c/examples/BazelBuildSamples/Messages.pbobjc.h"
#import "src/objective-c/examples/BazelBuildSamples/rmt/Test.pbrpc.h"
#endif
static NSString *const kPackage = @"grpc.testing";
static NSString *const kService = @"TestService";
@interface ViewController ()<GRPCResponseHandler>
@end
@implementation ViewController {
GRPCCallOptions *_options;
}
- (void)viewDidLoad {
[super viewDidLoad];
GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
// optionally modify options
_options = options;
}
- (IBAction)tapCall:(id)sender {
GRPCProtoMethod *kUnaryCallMethod =
[[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"UnaryCall"];
GRPCRequestOptions *requestOptions =
[[GRPCRequestOptions alloc] initWithHost:@"grpc-test.sandbox.googleapis.com"
path:kUnaryCallMethod.HTTPPath
safety:GRPCCallSafetyCacheableRequest];
GRPCCall2 *call = [[GRPCCall2 alloc] initWithRequestOptions:requestOptions
responseHandler:self
callOptions:_options];
RMTSimpleRequest *request = [RMTSimpleRequest message];
request.responseSize = 100;
[call start];
[call writeData:[request data]];
[call finish];
}
- (dispatch_queue_t)dispatchQueue {
return dispatch_get_main_queue();
}
- (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata {
NSLog(@"Header: %@", initialMetadata);
}
- (void)didReceiveData:(id)data {
NSLog(@"Message: %@", data);
}
- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error {
NSLog(@"Trailer: %@\nError: %@", trailingMetadata, error);
}
@end

@ -0,0 +1,26 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

@ -0,0 +1,118 @@
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Message definitions to be used by integration test service definitions.
syntax = "proto3";
package grpc.testing;
option objc_class_prefix = "RMT";
// The type of payload that should be returned.
enum PayloadType {
// Compressable text format.
COMPRESSABLE = 0;
// Uncompressable binary format.
UNCOMPRESSABLE = 1;
// Randomly chosen from all other formats defined in this enum.
RANDOM = 2;
}
// A block of data, to simply increase gRPC message size.
message Payload {
// The type of data in body.
PayloadType type = 1;
// Primary contents of payload.
bytes body = 2;
}
// Unary request.
message SimpleRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, server randomly chooses one from other formats.
PayloadType response_type = 1;
// Desired payload size in the response from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
int32 response_size = 2;
// Optional input payload sent along with the request.
Payload payload = 3;
// Whether SimpleResponse should include username.
bool fill_username = 4;
// Whether SimpleResponse should include OAuth scope.
bool fill_oauth_scope = 5;
}
// Unary response, as configured by the request.
message SimpleResponse {
// Payload to increase message size.
Payload payload = 1;
// The user the request came from, for verifying authentication was
// successful when the client expected it.
string username = 2;
// OAuth scope.
string oauth_scope = 3;
}
// Client-streaming request.
message StreamingInputCallRequest {
// Optional input payload sent along with the request.
Payload payload = 1;
// Not expecting any payload from the response.
}
// Client-streaming response.
message StreamingInputCallResponse {
// Aggregated size of payloads received from the client.
int32 aggregated_payload_size = 1;
}
// Configuration for a particular response.
message ResponseParameters {
// Desired payload sizes in responses from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
int32 size = 1;
// Desired interval between consecutive responses in the response stream in
// microseconds.
int32 interval_us = 2;
}
// Server-streaming request.
message StreamingOutputCallRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, the payload from each response in the stream
// might be of different types. This is to simulate a mixed type of payload
// stream.
PayloadType response_type = 1;
// Configuration for each expected response message.
repeated ResponseParameters response_parameters = 2;
// Optional input payload sent along with the request.
Payload payload = 3;
}
// Server-streaming response, as configured by the request and parameters.
message StreamingOutputCallResponse {
// Payload to increase response size.
Payload payload = 1;
}

@ -0,0 +1,28 @@
# gRPC Bazel BUILD file.
#
# Copyright 2019 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
licenses(["notice"]) # Apache v2
package(default_visibility = ["//visibility:public"])
exports_files(["LICENSE"])
proto_library(
name = "test_proto",
srcs = ["test.proto"],
deps = ["//src/objective-c/examples:messages_proto"],
visibility = ["//visibility:public"],
)

@ -0,0 +1,57 @@
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// An integration test service that covers all the method signature permutations
// of unary/streaming requests/responses.
syntax = "proto3";
import "google/protobuf/empty.proto";
import "src/objective-c/examples/BazelBuildSamples/messages.proto";
package grpc.testing;
option objc_class_prefix = "RMT";
// A simple service to test the various types of RPCs and experiment with
// performance with various types of payload.
service TestService {
// One empty request followed by one empty response.
rpc EmptyCall(google.protobuf.Empty) returns (google.protobuf.Empty);
// One request followed by one response.
rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
rpc StreamingOutputCall(StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
rpc StreamingInputCall(stream StreamingInputCallRequest)
returns (StreamingInputCallResponse);
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full duplexing.
rpc FullDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
rpc HalfDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
}

@ -13,6 +13,7 @@
# limitations under the License.
"""Invocation-side implementation of gRPC Python."""
import functools
import logging
import sys
import threading
@ -81,17 +82,6 @@ def _unknown_code_details(unknown_cygrpc_code, details):
unknown_cygrpc_code, details)
def _wait_once_until(condition, until):
if until is None:
condition.wait()
else:
remaining = until - time.time()
if remaining < 0:
raise grpc.FutureTimeoutError()
else:
condition.wait(timeout=remaining)
class _RPCState(object):
def __init__(self, due, initial_metadata, trailing_metadata, code, details):
@ -178,12 +168,11 @@ def _event_handler(state, response_deserializer):
#pylint: disable=too-many-statements
def _consume_request_iterator(request_iterator, state, call, request_serializer,
event_handler):
if cygrpc.is_fork_support_enabled():
condition_wait_timeout = 1.0
else:
condition_wait_timeout = None
"""Consume a request iterator supplied by the user."""
def consume_request_iterator(): # pylint: disable=too-many-branches
# Iterate over the request iterator until it is exhausted or an error
# condition is encountered.
while True:
return_from_user_request_generator_invoked = False
try:
@ -224,14 +213,19 @@ def _consume_request_iterator(request_iterator, state, call, request_serializer,
state.due.add(cygrpc.OperationType.send_message)
else:
return
while True:
state.condition.wait(condition_wait_timeout)
cygrpc.block_if_fork_in_progress(state)
if state.code is None:
if cygrpc.OperationType.send_message not in state.due:
break
else:
return
def _done():
return (state.code is not None or
cygrpc.OperationType.send_message not in
state.due)
_common.wait(
state.condition.wait,
_done,
spin_cb=functools.partial(
cygrpc.block_if_fork_in_progress, state))
if state.code is not None:
return
else:
return
with state.condition:
@ -281,13 +275,21 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call): # pylint: disable=too
with self._state.condition:
return self._state.code is not None
def _is_complete(self):
return self._state.code is not None
def result(self, timeout=None):
until = None if timeout is None else time.time() + timeout
"""Returns the result of the computation or raises its exception.
See grpc.Future.result for the full API contract.
"""
with self._state.condition:
while True:
if self._state.code is None:
_wait_once_until(self._state.condition, until)
elif self._state.code is grpc.StatusCode.OK:
timed_out = _common.wait(
self._state.condition.wait, self._is_complete, timeout=timeout)
if timed_out:
raise grpc.FutureTimeoutError()
else:
if self._state.code is grpc.StatusCode.OK:
return self._state.response
elif self._state.cancelled:
raise grpc.FutureCancelledError()
@ -295,12 +297,17 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call): # pylint: disable=too
raise self
def exception(self, timeout=None):
until = None if timeout is None else time.time() + timeout
"""Return the exception raised by the computation.
See grpc.Future.exception for the full API contract.
"""
with self._state.condition:
while True:
if self._state.code is None:
_wait_once_until(self._state.condition, until)
elif self._state.code is grpc.StatusCode.OK:
timed_out = _common.wait(
self._state.condition.wait, self._is_complete, timeout=timeout)
if timed_out:
raise grpc.FutureTimeoutError()
else:
if self._state.code is grpc.StatusCode.OK:
return None
elif self._state.cancelled:
raise grpc.FutureCancelledError()
@ -308,12 +315,17 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call): # pylint: disable=too
return self
def traceback(self, timeout=None):
until = None if timeout is None else time.time() + timeout
"""Access the traceback of the exception raised by the computation.
See grpc.future.traceback for the full API contract.
"""
with self._state.condition:
while True:
if self._state.code is None:
_wait_once_until(self._state.condition, until)
elif self._state.code is grpc.StatusCode.OK:
timed_out = _common.wait(
self._state.condition.wait, self._is_complete, timeout=timeout)
if timed_out:
raise grpc.FutureTimeoutError()
else:
if self._state.code is grpc.StatusCode.OK:
return None
elif self._state.cancelled:
raise grpc.FutureCancelledError()
@ -345,17 +357,23 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call): # pylint: disable=too
raise StopIteration()
else:
raise self
while True:
self._state.condition.wait()
if self._state.response is not None:
response = self._state.response
self._state.response = None
return response
elif cygrpc.OperationType.receive_message not in self._state.due:
if self._state.code is grpc.StatusCode.OK:
raise StopIteration()
elif self._state.code is not None:
raise self
def _response_ready():
return (
self._state.response is not None or
(cygrpc.OperationType.receive_message not in self._state.due
and self._state.code is not None))
_common.wait(self._state.condition.wait, _response_ready)
if self._state.response is not None:
response = self._state.response
self._state.response = None
return response
elif cygrpc.OperationType.receive_message not in self._state.due:
if self._state.code is grpc.StatusCode.OK:
raise StopIteration()
elif self._state.code is not None:
raise self
def __iter__(self):
return self
@ -386,32 +404,47 @@ class _Rendezvous(grpc.RpcError, grpc.Future, grpc.Call): # pylint: disable=too
def initial_metadata(self):
with self._state.condition:
while self._state.initial_metadata is None:
self._state.condition.wait()
def _done():
return self._state.initial_metadata is not None
_common.wait(self._state.condition.wait, _done)
return self._state.initial_metadata
def trailing_metadata(self):
with self._state.condition:
while self._state.trailing_metadata is None:
self._state.condition.wait()
def _done():
return self._state.trailing_metadata is not None
_common.wait(self._state.condition.wait, _done)
return self._state.trailing_metadata
def code(self):
with self._state.condition:
while self._state.code is None:
self._state.condition.wait()
def _done():
return self._state.code is not None
_common.wait(self._state.condition.wait, _done)
return self._state.code
def details(self):
with self._state.condition:
while self._state.details is None:
self._state.condition.wait()
def _done():
return self._state.details is not None
_common.wait(self._state.condition.wait, _done)
return _common.decode(self._state.details)
def debug_error_string(self):
with self._state.condition:
while self._state.debug_error_string is None:
self._state.condition.wait()
def _done():
return self._state.debug_error_string is not None
_common.wait(self._state.condition.wait, _done)
return _common.decode(self._state.debug_error_string)
def _repr(self):

@ -15,6 +15,7 @@
import logging
import time
import six
import grpc
@ -60,6 +61,8 @@ STATUS_CODE_TO_CYGRPC_STATUS_CODE = {
CYGRPC_STATUS_CODE_TO_STATUS_CODE)
}
MAXIMUM_WAIT_TIMEOUT = 0.1
def encode(s):
if isinstance(s, bytes):
@ -96,3 +99,50 @@ def deserialize(serialized_message, deserializer):
def fully_qualified_method(group, method):
return '/{}/{}'.format(group, method)
def _wait_once(wait_fn, timeout, spin_cb):
wait_fn(timeout=timeout)
if spin_cb is not None:
spin_cb()
def wait(wait_fn, wait_complete_fn, timeout=None, spin_cb=None):
"""Blocks waiting for an event without blocking the thread indefinitely.
See https://github.com/grpc/grpc/issues/19464 for full context. CPython's
`threading.Event.wait` and `threading.Condition.wait` methods, if invoked
without a timeout kwarg, may block the calling thread indefinitely. If the
call is made from the main thread, this means that signal handlers may not
run for an arbitrarily long period of time.
This wrapper calls the supplied wait function with an arbitrary short
timeout to ensure that no signal handler has to wait longer than
MAXIMUM_WAIT_TIMEOUT before executing.
Args:
wait_fn: A callable acceptable a single float-valued kwarg named
`timeout`. This function is expected to be one of `threading.Event.wait`
or `threading.Condition.wait`.
wait_complete_fn: A callable taking no arguments and returning a bool.
When this function returns true, it indicates that waiting should cease.
timeout: An optional float-valued number of seconds after which the wait
should cease.
spin_cb: An optional Callable taking no arguments and returning nothing.
This callback will be called on each iteration of the spin. This may be
used for, e.g. work related to forking.
Returns:
True if a timeout was supplied and it was reached. False otherwise.
"""
if timeout is None:
while not wait_complete_fn():
_wait_once(wait_fn, MAXIMUM_WAIT_TIMEOUT, spin_cb)
else:
end = time.time() + timeout
while not wait_complete_fn():
remaining = min(end - time.time(), MAXIMUM_WAIT_TIMEOUT)
if remaining < 0:
return True
_wait_once(wait_fn, remaining, spin_cb)
return False

@ -145,6 +145,8 @@ class TestGevent(setuptools.Command):
'unit._exit_test.ExitTest.test_in_flight_partial_unary_stream_call',
'unit._exit_test.ExitTest.test_in_flight_partial_stream_unary_call',
'unit._exit_test.ExitTest.test_in_flight_partial_stream_stream_call',
# TODO(https://github.com/grpc/grpc/issues/18980): Reenable.
'unit._signal_handling_test.SignalHandlingTest',
'unit._metadata_flags_test',
'health_check._health_servicer_test.HealthServicerTest.test_cancelled_watch_removed_from_watch_list',
# TODO(https://github.com/grpc/grpc/issues/17330) enable these three tests

@ -67,6 +67,7 @@
"unit._server_ssl_cert_config_test.ServerSSLCertReloadTestWithoutClientAuth",
"unit._server_test.ServerTest",
"unit._session_cache_test.SSLSessionCacheTest",
"unit._signal_handling_test.SignalHandlingTest",
"unit._version_test.VersionTest",
"unit.beta._beta_features_test.BetaFeaturesTest",
"unit.beta._beta_features_test.ContextManagementAndLifecycleTest",

@ -16,6 +16,7 @@ GRPCIO_TESTS_UNIT = [
"_credentials_test.py",
"_dns_resolver_test.py",
"_empty_message_test.py",
"_error_message_encoding_test.py",
"_exit_test.py",
"_interceptor_test.py",
"_invalid_metadata_test.py",
@ -27,6 +28,7 @@ GRPCIO_TESTS_UNIT = [
# "_reconnect_test.py",
"_resource_exhausted_test.py",
"_rpc_test.py",
"_signal_handling_test.py",
# TODO(ghostwriternr): To be added later.
# "_server_ssl_cert_config_test.py",
"_server_test.py",
@ -39,6 +41,11 @@ py_library(
srcs = ["_tcp_proxy.py"],
)
py_library(
name = "_signal_client",
srcs = ["_signal_client.py"],
)
py_library(
name = "resources",
srcs = ["resources.py"],
@ -87,6 +94,7 @@ py_library(
":_server_shutdown_scenarios",
":_from_grpc_import_star",
":_tcp_proxy",
":_signal_client",
"//src/python/grpcio_tests/tests/unit/framework/common",
"//src/python/grpcio_tests/tests/testing",
requirement('six'),

@ -0,0 +1,84 @@
# Copyright 2019 the gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Client for testing responsiveness to signals."""
from __future__ import print_function
import argparse
import functools
import logging
import signal
import sys
import grpc
SIGTERM_MESSAGE = "Handling sigterm!"
UNARY_UNARY = "/test/Unary"
UNARY_STREAM = "/test/ServerStreaming"
_MESSAGE = b'\x00\x00\x00'
_ASSERTION_MESSAGE = "Control flow should never reach here."
# NOTE(gnossen): We use a global variable here so that the signal handler can be
# installed before the RPC begins. If we do not do this, then we may receive the
# SIGINT before the signal handler is installed. I'm not happy with per-process
# global state, but the per-process global state that is signal handlers
# somewhat forces my hand.
per_process_rpc_future = None
def handle_sigint(unused_signum, unused_frame):
print(SIGTERM_MESSAGE)
if per_process_rpc_future is not None:
per_process_rpc_future.cancel()
sys.stderr.flush()
sys.exit(0)
def main_unary(server_target):
"""Initiate a unary RPC to be interrupted by a SIGINT."""
global per_process_rpc_future # pylint: disable=global-statement
with grpc.insecure_channel(server_target) as channel:
multicallable = channel.unary_unary(UNARY_UNARY)
signal.signal(signal.SIGINT, handle_sigint)
per_process_rpc_future = multicallable.future(
_MESSAGE, wait_for_ready=True)
result = per_process_rpc_future.result()
assert False, _ASSERTION_MESSAGE
def main_streaming(server_target):
"""Initiate a streaming RPC to be interrupted by a SIGINT."""
global per_process_rpc_future # pylint: disable=global-statement
with grpc.insecure_channel(server_target) as channel:
signal.signal(signal.SIGINT, handle_sigint)
per_process_rpc_future = channel.unary_stream(UNARY_STREAM)(
_MESSAGE, wait_for_ready=True)
for result in per_process_rpc_future:
pass
assert False, _ASSERTION_MESSAGE
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Signal test client.')
parser.add_argument('server', help='Server target')
parser.add_argument(
'arity', help='RPC arity', choices=('unary', 'streaming'))
args = parser.parse_args()
if args.arity == 'unary':
main_unary(args.server)
else:
main_streaming(args.server)

@ -0,0 +1,172 @@
# Copyright 2019 the gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Test of responsiveness to signals."""
import logging
import os
import signal
import subprocess
import tempfile
import threading
import unittest
import sys
import grpc
from tests.unit import test_common
from tests.unit import _signal_client
_CLIENT_PATH = None
if sys.executable is not None:
_CLIENT_PATH = os.path.abspath(os.path.realpath(_signal_client.__file__))
else:
# NOTE(rbellevi): For compatibility with internal testing.
if len(sys.argv) != 2:
raise RuntimeError("Must supply path to executable client.")
client_name = sys.argv[1].split("/")[-1]
del sys.argv[1] # For compatibility with test runner.
_CLIENT_PATH = os.path.realpath(
os.path.join(os.path.dirname(os.path.abspath(__file__)), client_name))
_HOST = 'localhost'
class _GenericHandler(grpc.GenericRpcHandler):
def __init__(self):
self._connected_clients_lock = threading.RLock()
self._connected_clients_event = threading.Event()
self._connected_clients = 0
self._unary_unary_handler = grpc.unary_unary_rpc_method_handler(
self._handle_unary_unary)
self._unary_stream_handler = grpc.unary_stream_rpc_method_handler(
self._handle_unary_stream)
def _on_client_connect(self):
with self._connected_clients_lock:
self._connected_clients += 1
self._connected_clients_event.set()
def _on_client_disconnect(self):
with self._connected_clients_lock:
self._connected_clients -= 1
if self._connected_clients == 0:
self._connected_clients_event.clear()
def await_connected_client(self):
"""Blocks until a client connects to the server."""
self._connected_clients_event.wait()
def _handle_unary_unary(self, request, servicer_context):
"""Handles a unary RPC.
Blocks until the client disconnects and then echoes.
"""
stop_event = threading.Event()
def on_rpc_end():
self._on_client_disconnect()
stop_event.set()
servicer_context.add_callback(on_rpc_end)
self._on_client_connect()
stop_event.wait()
return request
def _handle_unary_stream(self, request, servicer_context):
"""Handles a server streaming RPC.
Blocks until the client disconnects and then echoes.
"""
stop_event = threading.Event()
def on_rpc_end():
self._on_client_disconnect()
stop_event.set()
servicer_context.add_callback(on_rpc_end)
self._on_client_connect()
stop_event.wait()
yield request
def service(self, handler_call_details):
if handler_call_details.method == _signal_client.UNARY_UNARY:
return self._unary_unary_handler
elif handler_call_details.method == _signal_client.UNARY_STREAM:
return self._unary_stream_handler
else:
return None
def _read_stream(stream):
stream.seek(0)
return stream.read()
def _start_client(args, stdout, stderr):
invocation = None
if sys.executable is not None:
invocation = (sys.executable, _CLIENT_PATH) + tuple(args)
else:
invocation = (_CLIENT_PATH,) + tuple(args)
return subprocess.Popen(invocation, stdout=stdout, stderr=stderr)
class SignalHandlingTest(unittest.TestCase):
def setUp(self):
self._server = test_common.test_server()
self._port = self._server.add_insecure_port('{}:0'.format(_HOST))
self._handler = _GenericHandler()
self._server.add_generic_rpc_handlers((self._handler,))
self._server.start()
def tearDown(self):
self._server.stop(None)
@unittest.skipIf(os.name == 'nt', 'SIGINT not supported on windows')
def testUnary(self):
"""Tests that the server unary code path does not stall signal handlers."""
server_target = '{}:{}'.format(_HOST, self._port)
with tempfile.TemporaryFile(mode='r') as client_stdout:
with tempfile.TemporaryFile(mode='r') as client_stderr:
client = _start_client((server_target, 'unary'), client_stdout,
client_stderr)
self._handler.await_connected_client()
client.send_signal(signal.SIGINT)
self.assertFalse(client.wait(), msg=_read_stream(client_stderr))
client_stdout.seek(0)
self.assertIn(_signal_client.SIGTERM_MESSAGE,
client_stdout.read())
@unittest.skipIf(os.name == 'nt', 'SIGINT not supported on windows')
def testStreaming(self):
"""Tests that the server streaming code path does not stall signal handlers."""
server_target = '{}:{}'.format(_HOST, self._port)
with tempfile.TemporaryFile(mode='r') as client_stdout:
with tempfile.TemporaryFile(mode='r') as client_stderr:
client = _start_client((server_target, 'streaming'),
client_stdout, client_stderr)
self._handler.await_connected_client()
client.send_signal(signal.SIGINT)
self.assertFalse(client.wait(), msg=_read_stream(client_stderr))
client_stdout.seek(0)
self.assertIn(_signal_client.SIGTERM_MESSAGE,
client_stdout.read())
if __name__ == '__main__':
logging.basicConfig()
unittest.main(verbosity=2)

@ -63,6 +63,7 @@ _GRPC_DEP_NAMES = [
_ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME,
_TWISTED_CONSTANTLY_DEP_NAME,
'io_bazel_rules_go',
'build_bazel_rules_apple',
]
_GRPC_BAZEL_ONLY_DEPS = [
@ -76,6 +77,7 @@ _GRPC_BAZEL_ONLY_DEPS = [
_ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME,
_TWISTED_CONSTANTLY_DEP_NAME,
'io_bazel_rules_go',
'build_bazel_rules_apple',
]
@ -106,6 +108,13 @@ class BazelEvalState(object):
return
self.names_and_urls[args['name']] = args['url']
def git_repository(self, **args):
assert self.names_and_urls.get(args['name']) is None
if args['name'] in _GRPC_BAZEL_ONLY_DEPS:
self.names_and_urls[args['name']] = 'dont care'
return
self.names_and_urls[args['name']] = args['remote']
# Parse git hashes from bazel/grpc_deps.bzl {new_}http_archive rules
with open(os.path.join('bazel', 'grpc_deps.bzl'), 'r') as f:
@ -121,6 +130,7 @@ build_rules = {
'native': eval_state,
'http_archive': lambda **args: eval_state.http_archive(**args),
'load': lambda a, b: None,
'git_repository': lambda **args: eval_state.git_repository(**args),
}
exec bazel_file in build_rules
for name in _GRPC_DEP_NAMES:
@ -162,6 +172,7 @@ for name in _GRPC_DEP_NAMES:
'native': state,
'http_archive': lambda **args: state.http_archive(**args),
'load': lambda a, b: None,
'git_repository': lambda **args: state.git_repository(**args),
}
exec bazel_file in rules
assert name not in names_and_urls_with_overridden_name.keys()

Loading…
Cancel
Save