|
3 | 3 |
|
4 | 4 | #include "bst/test/test.h" |
5 | 5 | #include "sdp/json.h" |
| 6 | +#include "sdp/sdp_grammar.h" |
6 | 7 |
|
7 | 8 | //////////////////////////////////////////////////////////////////////////////////////////// |
8 | 9 | BST_TEST_CASE(testSdpRoundtrip) |
@@ -1120,3 +1121,93 @@ a=mid:SECONDARY |
1120 | 1121 | } while (!expected.fail() && !actual.fail()); |
1121 | 1122 | } |
1122 | 1123 | } |
| 1124 | + |
| 1125 | +//////////////////////////////////////////////////////////////////////////////////////////// |
| 1126 | +namespace |
| 1127 | +{ |
| 1128 | + // an example application-defined attribute |
| 1129 | + // a=x-example-foo:<format> <bar> |
| 1130 | + const utility::string_t example_foo{ U("x-example-foo") }; |
| 1131 | + |
| 1132 | + namespace example_fields |
| 1133 | + { |
| 1134 | + const web::json::field_as_integer format{ U("format") }; |
| 1135 | + const web::json::field_as_string bar{ U("bar") }; |
| 1136 | + } |
| 1137 | + |
| 1138 | + // session_description captures converters by reference, so they must outlive |
| 1139 | + // the returned grammar; a function-local static is the natural application pattern |
| 1140 | + const sdp::grammar::attribute_converters& example_attribute_converters() |
| 1141 | + { |
| 1142 | + static const auto converters = [] { |
| 1143 | + auto converters = sdp::grammar::get_default_attribute_converters(); |
| 1144 | + converters[example_foo] = sdp::grammar::object_converter({ |
| 1145 | + { example_fields::format, sdp::grammar::digits_converter }, |
| 1146 | + { example_fields::bar, sdp::grammar::string_converter } |
| 1147 | + }); |
| 1148 | + return converters; |
| 1149 | + }(); |
| 1150 | + return converters; |
| 1151 | + } |
| 1152 | + |
| 1153 | + const sdp::grammar::description& example_grammar() |
| 1154 | + { |
| 1155 | + static const auto grammar = sdp::grammar::session_description( |
| 1156 | + example_attribute_converters(), |
| 1157 | + sdp::grammar::default_attribute_converter |
| 1158 | + ); |
| 1159 | + return grammar; |
| 1160 | + } |
| 1161 | +} |
| 1162 | + |
| 1163 | +BST_TEST_CASE(testSdpApplicationDefinedAttributes) |
| 1164 | +{ |
| 1165 | + const auto& grammar = example_grammar(); |
| 1166 | + |
| 1167 | + const std::string test_sdp = R"(v=0 |
| 1168 | +o=- 3745911798 3745911798 IN IP4 192.168.9.142 |
| 1169 | +s=Example Sender 1 (Video) |
| 1170 | +t=0 0 |
| 1171 | +a=x-example-foo:0 session-level |
| 1172 | +m=video 50020 RTP/AVP 96 |
| 1173 | +c=IN IP4 239.22.142.1/32 |
| 1174 | +a=rtpmap:96 raw/90000 |
| 1175 | +a=x-example-foo:96 media-level |
| 1176 | +a=x-example-bar:unknown attributes are still handled by the default converter |
| 1177 | +a=recvonly |
| 1178 | +)"; |
| 1179 | + |
| 1180 | + auto session_description = sdp::parse_session_description(test_sdp, grammar); |
| 1181 | + |
| 1182 | + auto& session_attributes = sdp::fields::attributes(session_description).as_array(); |
| 1183 | + auto session_foo = sdp::find_name(session_attributes, example_foo); |
| 1184 | + BST_REQUIRE(session_attributes.end() != session_foo); |
| 1185 | + BST_REQUIRE_EQUAL(0, example_fields::format(sdp::fields::value(*session_foo))); |
| 1186 | + BST_REQUIRE_EQUAL(U("session-level"), example_fields::bar(sdp::fields::value(*session_foo))); |
| 1187 | + |
| 1188 | + auto& media_attributes = sdp::fields::attributes(sdp::fields::media_descriptions(session_description).at(0)).as_array(); |
| 1189 | + auto media_foo = sdp::find_name(media_attributes, example_foo); |
| 1190 | + BST_REQUIRE(media_attributes.end() != media_foo); |
| 1191 | + BST_REQUIRE_EQUAL(96, example_fields::format(sdp::fields::value(*media_foo))); |
| 1192 | + BST_REQUIRE_EQUAL(U("media-level"), example_fields::bar(sdp::fields::value(*media_foo))); |
| 1193 | + |
| 1194 | + auto media_bar = sdp::find_name(media_attributes, U("x-example-bar")); |
| 1195 | + BST_REQUIRE(media_attributes.end() != media_bar); |
| 1196 | + BST_REQUIRE_EQUAL(U("unknown attributes are still handled by the default converter"), sdp::fields::value(*media_bar).as_string()); |
| 1197 | + |
| 1198 | + auto recvonly = sdp::find_name(media_attributes, sdp::attributes::recvonly); |
| 1199 | + BST_REQUIRE(media_attributes.end() != recvonly); |
| 1200 | + BST_REQUIRE(sdp::fields::value(*recvonly).is_null()); |
| 1201 | + |
| 1202 | + auto test_sdp2 = sdp::make_session_description(session_description, grammar); |
| 1203 | + std::istringstream expected(test_sdp), actual(test_sdp2); |
| 1204 | + do |
| 1205 | + { |
| 1206 | + std::string expected_line, actual_line; |
| 1207 | + std::getline(expected, expected_line); |
| 1208 | + std::getline(actual, actual_line); |
| 1209 | + // CR cannot appear in a raw string literal, so remove it from the actual line |
| 1210 | + if (!actual_line.empty() && '\r' == actual_line.back()) actual_line.pop_back(); |
| 1211 | + BST_CHECK_EQUAL(expected_line, actual_line); |
| 1212 | + } while (!expected.fail() && !actual.fail()); |
| 1213 | +} |
0 commit comments