implemented (and tested) printing out whole messages/structures
diff --git a/aos/build/queues/compiler.rb b/aos/build/queues/compiler.rb
index e53e217..899ba85 100644
--- a/aos/build/queues/compiler.rb
+++ b/aos/build/queues/compiler.rb
@@ -58,6 +58,14 @@
 		exit!(-1)
 	end
 end
+def format_pipeline(output)
+  read_in, write_in = IO.pipe()
+  child = Process.spawn('clang-format-3.4 --style=google',
+                        {:in=>read_in, write_in=>:close,
+                         :out=>output})
+  read_in.close
+  [child, write_in]
+end
 def build(filename,globals_template)
 	globals = Globals.new()
 	globals_template.paths.each do |path|
@@ -85,10 +93,20 @@
 
 	header_file = File.open(h_file_path,"w+")
 	cc_file = File.open(cc_file_path,"w+")
-	cpp_tree.write_header_file($cpp_base,header_file)
-	cpp_tree.write_cc_file($cpp_base,cc_file)
-	cc_file.close()
-	header_file.close()
+  header_child, header_output = format_pipeline(header_file)
+  cc_child, cc_output = format_pipeline(cc_file)
+	cpp_tree.write_header_file($cpp_base,header_output)
+	cpp_tree.write_cc_file($cpp_base,cc_output)
+	cc_output.close()
+	header_output.close()
+  if !Process.wait2(cc_child)[1].success?
+    $stderr.puts "Formatting cc file failed."
+    exit 1
+  end
+  if !Process.wait2(header_child)[1].success?
+    $stderr.puts "Formatting header file failed."
+    exit 1
+  end
 end
 begin
 	args = ARGV.dup
diff --git a/aos/build/queues/cpp_pretty_print/dep_file_pair.rb b/aos/build/queues/cpp_pretty_print/dep_file_pair.rb
index 4c9fb1a..c13bb9e 100644
--- a/aos/build/queues/cpp_pretty_print/dep_file_pair.rb
+++ b/aos/build/queues/cpp_pretty_print/dep_file_pair.rb
@@ -182,6 +182,9 @@
 			@protections = {}
 			@deps = []
 		end
+    def parent_class
+      @parent.split(' ')[1] if @parent
+    end
 		class ProtectionGroup
 			def initialize(name)
 				@name = name
diff --git a/aos/build/queues/output/message_dec.rb b/aos/build/queues/output/message_dec.rb
index f1a6375..1666298 100644
--- a/aos/build/queues/output/message_dec.rb
+++ b/aos/build/queues/output/message_dec.rb
@@ -24,12 +24,81 @@
 			member.add_TypeRegister(cpp_tree, type_class, member_func)
     end
 		id = getTypeID()
-		member_func.suite << ("static const ::aos::MessageType kMsgMessageType(#{id}, #{@name.inspect}, {" +
+		member_func.suite << ("static const ::aos::MessageType kMsgMessageType(#{type_class.parent_class ? type_class.parent_class + '::Size()' : 0}, #{id}, #{@loc.queue_name(@name).inspect}, {" +
 		  "#{fields.join(", ")}})");
 		type_class.add_member(member_func)
 		member_func.suite << "::aos::type_cache::Add(kMsgMessageType)"
 		member_func.suite << CPP::Return.new("&kMsgMessageType")
 	end
+        def create_InOrderConstructor(type_class, cpp_tree)
+          cons = CPP::Constructor.new(type_class)
+          type_class.add_member(cons)
+          @members.each do |member|
+            if member.respond_to?(:type_name)
+              type_name = member.type_name(cpp_tree)
+            else
+              type_name = member.type
+            end
+
+            cons.args << "#{type_name} #{member.name}_in"
+            cons.add_cons(member.name, member.name + '_in')
+          end
+        end
+        def create_DefaultConstructor(type_class, cpp_tree)
+          cons = CPP::Constructor.new(type_class)
+          type_class.add_member(cons)
+          cons.add_cons(type_class.parent_class) if type_class.parent_class
+          cons.suite << CPP::FuncCall.build('Zero')
+        end
+	def create_Zero(type_class,cpp_tree)
+		member_func = CPP::MemberFunc.new(type_class,"void","Zero")
+		type_class.add_member(member_func)
+		@members.each do |elem|
+			elem.zeroCall(member_func.suite)
+		end
+                member_func.suite << CPP::FuncCall.new(type_class.parent_class + '::Zero') if type_class.parent_class
+	end
+	def create_Size(type_class,cpp_tree)
+		member_func = CPP::MemberFunc.new(type_class,"size_t","Size")
+		member_func.inline = true
+        member_func.static = true
+		type_class.add_member(member_func.forward_dec)
+		size = 0
+		@members.each do |elem|
+			size += elem.size
+		end
+                if type_class.parent_class
+		  member_func.suite << CPP::Return.new(CPP::Add.new(size,
+							"#{type_class.parent_class}::Size()"))
+                else
+                  member_func.suite << CPP::Return.new(size)
+                end
+	end
+	def create_Serialize(type_class,cpp_tree)
+		member_func = CPP::MemberFunc.new(type_class,"size_t","Serialize")
+		type_class.add_member(member_func)
+		member_func.args << "char *buffer"
+		member_func.suite << "#{type_class.parent_class}::Serialize(buffer)" if type_class.parent_class
+		member_func.const = true
+		offset = type_class.parent_class ? type_class.parent_class + '::Size()' : '0'
+		@members.each do |elem|
+			elem.toNetwork(offset,member_func.suite)
+			offset += " + #{elem.size}";
+		end
+                member_func.suite << CPP::Return.new(CPP::FuncCall.new('Size'))
+	end
+	def create_Deserialize(type_class,cpp_tree)
+		member_func = CPP::MemberFunc.new(type_class,"size_t","Deserialize")
+		type_class.add_member(member_func)
+		member_func.args << "const char *buffer"
+		member_func.suite << "#{type_class.parent_class}::Deserialize(buffer)" if type_class.parent_class
+		offset = type_class.parent_class ? type_class.parent_class + '::Size()' : '0'
+		@members.each do |elem|
+			elem.toHost(offset,member_func.suite)
+			offset += " + #{elem.size}";
+		end
+                member_func.suite << CPP::Return.new(CPP::FuncCall.new('Size'))
+	end
 	def simpleStr()
 		return "{\n" + @members.collect() { |elem| elem.simpleStr() + "\n"}.join("") + "}"
 	end
@@ -84,55 +153,6 @@
     member_func.suite << "length -= super_size"
     member_func.suite << "return super_size + snprintf(buffer, length, " + ([format] + args).join(", ") + ")";
 	end
-	def create_Serialize(type_class,cpp_tree)
-		member_func = CPP::MemberFunc.new(type_class,"size_t","Serialize")
-		type_class.add_member(member_func)
-		#cpp_tree.cc_file.add_funct(member_func)
-		member_func.args << "char *buffer"
-		member_func.suite << "::aos::Message::Serialize(buffer)"
-		member_func.const = true
-		offset = 0
-		@members.each do |elem|
-			elem.toNetwork(offset,member_func.suite)
-			offset += elem.size;
-		end
-                member_func.suite << "return Size()"
-	end
-	def create_Deserialize(type_class,cpp_tree)
-		member_func = CPP::MemberFunc.new(type_class,"size_t","Deserialize")
-		type_class.add_member(member_func)
-		#cpp_tree.cc_file.add_funct(member_func)
-		member_func.args << "const char *buffer"
-		member_func.suite << "::aos::Message::Deserialize(buffer)"
-		offset = 0
-		@members.each do |elem|
-			elem.toHost(offset,member_func.suite)
-			offset += elem.size;
-		end
-                member_func.suite << "return Size()"
-	end
-	def create_Zero(type_class,cpp_tree)
-		member_func = CPP::MemberFunc.new(type_class,"void","Zero")
-		type_class.add_member(member_func)
-		#cpp_tree.cc_file.add_funct(member_func)
-		@members.each do |elem|
-			elem.zeroCall(member_func.suite)
-		end
-		member_func.suite << "::aos::Message::Zero()"
-	end
-	def create_Size(type_class,cpp_tree)
-		member_func = CPP::MemberFunc.new(type_class,"size_t","Size")
-		member_func.inline = true
-        member_func.static = true
-		type_class.add_member(member_func.forward_dec)
-		#cpp_tree.cc_file.add_funct(member_func)
-		size = 0
-		@members.each do |elem|
-			size += elem.size
-		end
-		member_func.suite << CPP::Return.new(CPP::Add.new(size,
-							"::aos::Message::Size()"))
-	end
 	def create_GetType(type_class, cpp_tree)
 		member_func = CPP::MemberFunc.new(type_class,"const ::aos::MessageType*","GetType")
 		type_class.add_member(member_func)
@@ -179,6 +199,8 @@
 		create_Print(type_class,cpp_tree)
 		create_GetType(type_class, cpp_tree)
 		create_DoGetType(type_class, cpp_tree)
+    create_DefaultConstructor(type_class, cpp_tree)
+    create_InOrderConstructor(type_class, cpp_tree)
 
 		b_namespace = cpp_tree.get(b_loc = self.class.builder_loc(@loc))
 
@@ -250,16 +272,14 @@
 		"#{@type} #{@name}"
 	end
 	def toNetwork(offset,suite, parent = "")
-		offset = (offset == 0) ? "" : "#{offset} + "
 		suite << f_call = CPP::FuncCall.build("to_network",
 									 "&#{parent}#{@name}",
-									 "&buffer[#{offset}::aos::Message::Size()]")
+									 "&buffer[#{offset}]")
 		f_call.args.dont_wrap = true
 	end
 	def toHost(offset,suite, parent = "")
-		offset = (offset == 0) ? "" : "#{offset} + "
 		suite << f_call = CPP::FuncCall.build("to_host",
-									 "&buffer[#{offset}::aos::Message::Size()]",
+									 "&buffer[#{offset}]",
 									 "&#{parent}#{@name}")
 		f_call.args.dont_wrap = true
 	end
diff --git a/aos/build/queues/output/q_struct.rb b/aos/build/queues/output/q_struct.rb
index 868f1f2..05c2932 100644
--- a/aos/build/queues/output/q_struct.rb
+++ b/aos/build/queues/output/q_struct.rb
@@ -29,6 +29,12 @@
 		end
 		create_DoGetType(type_class, cpp_tree)
 		create_GetType(type_class, cpp_tree)
+    create_DefaultConstructor(type_class, cpp_tree)
+    create_InOrderConstructor(type_class, cpp_tree)
+    create_Zero(type_class, cpp_tree)
+    create_Size(type_class, cpp_tree)
+    create_Serialize(type_class, cpp_tree)
+    create_Deserialize(type_class, cpp_tree)
 		return type_class
 	end
 	def getPrintFormat()
@@ -42,13 +48,13 @@
 	def toHost(offset, suite, parent)
 		@members.each do |elem|
 			elem.toHost(offset, suite, parent)
-			offset += elem.size()
+			offset += " + #{elem.size()}"
 		end
 	end
 	def toNetwork(offset, suite, parent)
 		@members.each do |elem|
 			elem.toNetwork(offset, suite, parent)
-			offset += elem.size()
+			offset += " + #{elem.size()}"
 		end
 	end
 	def zeroCall(suite, parent)
diff --git a/aos/common/queue.h b/aos/common/queue.h
index 210c61e..65bfa95 100644
--- a/aos/common/queue.h
+++ b/aos/common/queue.h
@@ -21,6 +21,8 @@
 class MessageType;
 
 // This class is a base class for all messages sent over queues.
+// All of the methods are overloaded in (generated) subclasses to do the same
+// thing for the whole thing.
 class Message {
  public:
   typedef ::aos::time::Time Time;
@@ -30,17 +32,13 @@
   Message() : sent_time(0, 0) {}
 
   // Zeros out the time.
-  // Overriden to zero the whole message.
   void Zero();
   // Returns the size of the common fields.
-  // Overriden to return the size of the whole message.
   static size_t Size() { return sizeof(Time); }
 
   // Deserializes the common fields from the buffer.
-  // Overriden to deserialize the whole message.
   size_t Deserialize(const char *buffer);
   // Serializes the common fields into the buffer.
-  // Overriden to serialize the whole message.
   size_t Serialize(char *buffer) const;
 
   // Populates sent_time with the current time.
diff --git a/aos/common/queue_types.cc b/aos/common/queue_types.cc
index 0183510..a9224f6 100644
--- a/aos/common/queue_types.cc
+++ b/aos/common/queue_types.cc
@@ -15,34 +15,39 @@
 
 ssize_t MessageType::Serialize(char *buffer, size_t max_bytes) const {
   char *const buffer_start = buffer;
-  size_t name_length = strlen(name);
-  ::std::unique_ptr<size_t> field_name_lengths(new size_t[number_fields]);
   size_t fields_size = 0;
   for (int i = 0; i < number_fields; ++i) {
     fields_size += sizeof(fields[i]->type);
-    field_name_lengths.get()[i] = strlen(fields[i]->name);
-    fields_size += field_name_lengths.get()[i];
+    fields_size += sizeof(size_t);
+    fields_size += fields[i]->name.size();
   }
-  if (max_bytes < sizeof(id) + sizeof(name_length) + sizeof(number_fields) +
-                      name_length + fields_size) {
+  if (max_bytes < sizeof(id) + sizeof(super_size) + sizeof(size_t) +
+                      sizeof(number_fields) + name.size() + fields_size) {
     return -1;
   }
+
+  size_t length;
+
+  to_network(&super_size, buffer);
+  buffer += sizeof(super_size);
   to_network(&id, buffer);
   buffer += sizeof(id);
-  to_network(&name_length, buffer);
-  buffer += sizeof(name_length);
+  length = name.size();
+  to_network(&length, buffer);
+  buffer += sizeof(length);
   to_network(&number_fields, buffer);
   buffer += sizeof(number_fields);
-  memcpy(buffer, name, name_length);
-  buffer += name_length;
+  memcpy(buffer, name.data(), name.size());
+  buffer += name.size();
 
   for (int i = 0; i < number_fields; ++i) {
     to_network(&fields[i]->type, buffer);
     buffer += sizeof(fields[i]->type);
-    to_network(&field_name_lengths.get()[i], buffer);
-    buffer += sizeof(field_name_lengths.get()[i]);
-    memcpy(buffer, fields[i]->name, field_name_lengths.get()[i]);
-    buffer += field_name_lengths.get()[i];
+    length = fields[i]->name.size();
+    to_network(&length, buffer);
+    buffer += sizeof(length);
+    memcpy(buffer, fields[i]->name.data(), fields[i]->name.size());
+    buffer += length;
   }
 
   return buffer - buffer_start;
@@ -50,13 +55,18 @@
 
 MessageType *MessageType::Deserialize(const char *buffer, size_t *bytes) {
   size_t name_length;
+  decltype(MessageType::super_size) super_size;
   decltype(MessageType::id) id;
   decltype(MessageType::number_fields) number_fields;
-  if (*bytes < sizeof(id) + sizeof(name_length) + sizeof(number_fields)) {
+  if (*bytes < sizeof(super_size) + sizeof(id) + sizeof(name_length) +
+                   sizeof(number_fields)) {
     return nullptr;
   }
-  *bytes -= sizeof(id) + sizeof(name_length) + sizeof(number_fields);
+  *bytes -= sizeof(super_size) + sizeof(id) + sizeof(name_length) +
+            sizeof(number_fields);
 
+  to_host(buffer, &super_size);
+  buffer += sizeof(super_size);
   to_host(buffer, &id);
   buffer += sizeof(id);
   to_host(buffer, &name_length);
@@ -70,13 +80,10 @@
   *bytes -= name_length;
 
   Field **fields = new Field *[number_fields];
-  ::std::unique_ptr<MessageType> r(new MessageType(number_fields, fields));
-  r->id = id;
-  ::std::unique_ptr<char> name(new char[name_length + 1]);
-  memcpy(name.get(), buffer, name_length);
+  ::std::unique_ptr<MessageType> r(
+      new MessageType(super_size, id, ::std::string(buffer, name_length),
+                      number_fields, fields));
   buffer += name_length;
-  name.get()[name_length] = '\0';
-  r->name = name.release();
 
   for (int i = 0; i < number_fields; ++i) {
     size_t field_name_length;
@@ -94,16 +101,67 @@
       return nullptr;
     }
     *bytes -= field_name_length;
-    ::std::unique_ptr<char> field_name(new char[field_name_length + 1]);
-    memcpy(field_name.get(), buffer, field_name_length);
+    fields[i]->name = ::std::string(buffer, field_name_length);
     buffer += field_name_length;
-    field_name.get()[field_name_length] = '\0';
-    fields[i]->name = field_name.release();
   }
 
   return r.release();
 }
 
+bool PrintMessage(char *output, size_t *output_bytes, void *input,
+                  size_t *input_bytes, const MessageType &type) {
+  *input_bytes -= type.super_size;
+  input = static_cast<char *>(input) + type.super_size;
+
+  if (*output_bytes < type.name.size() + 1) return false;
+  *output_bytes -= type.name.size() + 1;
+  memcpy(output, type.name.data(), type.name.size());
+  output += type.name.size();
+  *(output++) = '{';
+
+  bool first = true;
+  for (int i = 0; i < type.number_fields; ++i) {
+    if (first) {
+      first = false;
+    } else {
+      if (*output_bytes < 1) return false;
+      *output_bytes -= 1;
+      *(output++) = ',';
+    }
+
+    if (*output_bytes < type.fields[i]->name.size() + 1) return false;
+    *output_bytes -= type.fields[i]->name.size() + 1;
+    memcpy(output, type.fields[i]->name.data(), type.fields[i]->name.size());
+    output += type.fields[i]->name.size();
+    *(output++) = ':';
+
+    const size_t output_bytes_before = *output_bytes,
+                 input_bytes_before = *input_bytes;
+    if (MessageType::IsPrimitive(type.fields[i]->type)) {
+      if (!PrintField(output, output_bytes, input, input_bytes,
+                      type.fields[i]->type)) {
+        return false;
+      }
+    } else {
+      if (!PrintMessage(output, output_bytes, input, input_bytes,
+                        type_cache::Get(type.fields[i]->type))) {
+        return false;
+      }
+    }
+
+    // Update the input and output pointers, ignoring the trailing '\0' that the
+    // subcall put on.
+    output += output_bytes_before - *output_bytes - 1;
+    *output_bytes += 1;
+    input = static_cast<char *>(input) + input_bytes_before - *input_bytes;
+  }
+  if (*output_bytes < 2) return false;
+  *output_bytes -= 2;
+  *(output++) = '}';
+  *(output++) = '\0';
+  return true;
+}
+
 namespace type_cache {
 namespace {
 
@@ -186,7 +244,7 @@
   ssize_t size = cached.type.Serialize(buffer, sizeof(buffer));
   if (size == -1) {
     LOG(FATAL, "type %s is too big to fit into %zd bytes\n",
-        cached.type.name, sizeof(buffer));
+        cached.type.name.c_str(), sizeof(buffer));
   }
 
   volatile ShmType *shm =
diff --git a/aos/common/queue_types.h b/aos/common/queue_types.h
index cf7287f..439fc7f 100644
--- a/aos/common/queue_types.h
+++ b/aos/common/queue_types.h
@@ -6,28 +6,12 @@
 #include <string.h>
 
 #include <initializer_list>
+#include <string>
 
 #include "aos/common/macros.h"
 
 namespace aos {
 
-// Prints the value from 1 message field into output.
-// output is where to write the text representation.
-// output_bytes should point to the number of bytes available to write in
-// output. It will be reduced by the number of bytes that were actually written.
-// input is where to read the data in from (in network byte order, aka from
-// Serialize).
-// input_bytes should point to the number of bytes available to read from input.
-// It will be reduced by the number of bytes that were actually read.
-// type is the ID of a type to print. It must be a primitive type.
-//
-// Returns true for success and false for not.
-//
-// The implementation of this is generated by the ruby code.
-bool PrintField(char *output, size_t *output_bytes, void *input,
-                size_t *input_bytes, uint32_t type)
-    __attribute__((warn_unused_result));
-
 // The type IDs this uses are 2 parts: a 16 bit size and a 16 bit hash. Sizes
 // for primitive types are stored with 8192 (0x2000) added.
 //
@@ -36,14 +20,13 @@
   struct Field {
     // The type ID for the type of this field.
     uint32_t type;
-    const char *name;
+    ::std::string name;
   };
 
-  // Constructs a MessageType that doesn't own the storage for any of its
-  // names.
-  MessageType(uint32_t id, const char *name,
+  // Takes ownership of all the Field pointers in fields_initializer.
+  MessageType(size_t super_size, uint32_t id, const ::std::string &name,
               ::std::initializer_list<const Field *> fields_initializer)
-      : id(id), name(name), owns_names(false) {
+      : super_size(super_size), id(id), name(name) {
     number_fields = fields_initializer.size();
     fields = new const Field *[number_fields];
     int i = 0;
@@ -54,14 +37,8 @@
 
   ~MessageType() {
     for (int i = 0; i < number_fields; ++i) {
-      if (owns_names) {
-        delete fields[i]->name;
-      }
       delete fields[i];
     }
-    if (owns_names) {
-      delete name;
-    }
   }
 
   // Returns -1 if max_bytes is too small.
@@ -75,29 +52,56 @@
     return (type_id & 0x2000) != 0;
   }
 
+  // How many (serialized) bytes the superclass takes up.
+  size_t super_size;
   // The type ID for this.
   uint32_t id;
-  const char *name;
+  ::std::string name;
 
-  int32_t number_fields;
+  int number_fields;
   const Field **fields;
 
  private:
-  MessageType(int32_t number_fields, Field **fields)
-      : name(nullptr),
+  // Internal constructor for Deserialize to use.
+  MessageType(size_t super_size, uint32_t id, ::std::string &&name,
+              int number_fields, Field **fields)
+      : super_size(super_size),
+        id(id),
+        name(name),
         number_fields(number_fields),
-        fields(const_cast<const Field **>(fields)),
-        owns_names(true) {
+        fields(const_cast<const Field **>(fields)) {
     for (int i = 0; i < number_fields; ++i) {
       fields[i] = new Field();
     }
   }
 
-  const bool owns_names;
-
   DISALLOW_COPY_AND_ASSIGN(MessageType);
 };
 
+// The arguments are the same for both of these.
+//
+// output is where to write the text representation ('\0'-terminated
+// human-readable string).
+// output_bytes should point to the number of bytes available to write in
+// output. It will be reduced by the number of bytes that were actually written.
+// input is where to read the data in from (in network byte order, aka from
+// Serialize).
+// input_bytes should point to the number of bytes available to read from input.
+// It will be reduced by the number of bytes that were actually read.
+// type is which type to print.
+// Returns true for success and false for not.
+//
+// Prints the value from 1 primitive message field into output.
+// The implementation of this is generated by the ruby code.
+bool PrintField(char *output, size_t *output_bytes, void *input,
+                size_t *input_bytes, uint32_t type)
+    __attribute__((warn_unused_result));
+// Prints a whole message into output.
+// This calls itself recursively and PrintField to print out the whole thing.
+bool PrintMessage(char *output, size_t *output_bytes, void *input,
+                  size_t *input_bytes, const MessageType &type)
+    __attribute__((warn_unused_result));
+
 // Implements a cache of types which generally works per-process but can (when
 // instructed) put a type in shared memory which other processes will
 // automatically be able to retrieve.
diff --git a/aos/common/queue_types_test.cc b/aos/common/queue_types_test.cc
index a23c33b..c665333 100644
--- a/aos/common/queue_types_test.cc
+++ b/aos/common/queue_types_test.cc
@@ -9,13 +9,14 @@
 
 using ::aos::common::testing::Structure;
 using ::aos::common::testing::MessageWithStructure;
+using ::aos::common::testing::OtherTestingMessage;
 
 namespace aos {
 namespace testing {
 
 typedef MessageType::Field Field;
 
-static const MessageType kTestType1(0x1234, "TestType1",
+static const MessageType kTestType1(5, 0x1234, "TestType1",
                                     {new Field{0, "field1"},
                                      new Field{0, "field2"},
                                      new Field{0, "field3"}});
@@ -27,7 +28,7 @@
     if (l.id != r.id) {
       return AssertionFailure() << "id " << l.id << " != " << r.id;
     }
-    if (strcmp(l.name, r.name) != 0) {
+    if (l.name != r.name) {
       return AssertionFailure() << "name '" << l.name << "' != '" << r.name
                                 << "'";
     }
@@ -41,7 +42,7 @@
         return AssertionFailure() << "type " << l.fields[i]->type
                                   << " != " << r.fields[i]->type;
       }
-      if (strcmp(l.fields[i]->name, r.fields[i]->name) != 0) {
+      if (l.fields[i]->name != r.fields[i]->name) {
         return AssertionFailure() << "name '" << l.fields[i]->name << "' != '"
                                   << r.fields[i]->name << "'";
       }
@@ -57,7 +58,8 @@
   ::std::unique_ptr<MessageType> deserialized;
 
   size = kTestType1.Serialize(buffer, sizeof(buffer));
-  EXPECT_GT(size, 1);
+  ASSERT_GT(size, 1);
+  ASSERT_LE(static_cast<size_t>(size), sizeof(buffer));
 
   out_size = size;
   deserialized.reset(MessageType::Deserialize(buffer, &out_size));
@@ -80,6 +82,7 @@
   char input[128], output[128];
   size_t input_bytes, output_bytes;
 };
+typedef PrintFieldTest PrintMessageTest;
 
 TEST_F(PrintFieldTest, Basic) {
   static const uint16_t kData = 971;
@@ -127,5 +130,49 @@
                           Structure::GetType()->fields[1]->type));
 }
 
+static const OtherTestingMessage kTestMessage1(true, 8971, 3.2);
+static const ::std::string kTestMessage1String =
+    ".aos.common.testing.OtherTestingMessage{test_bool:T,test_int:8971"
+    ",test_double:3.200000}";
+static const Structure kTestStructure1(false, 973, 8.56);
+static const ::std::string kTestStructure1String =
+    ".aos.common.testing.Structure{struct_bool:f,struct_int:973"
+    ",struct_float:8.560000}";
+
+TEST_F(PrintMessageTest, Basic) {
+  assert(sizeof(input) >= kTestMessage1.Size());
+  input_bytes = kTestMessage1.Serialize(input);
+  output_bytes = sizeof(output);
+  ASSERT_TRUE(PrintMessage(output, &output_bytes, input, &input_bytes,
+                           *kTestMessage1.GetType()));
+  EXPECT_EQ(kTestMessage1String, ::std::string(output));
+  EXPECT_EQ(kTestMessage1String.size() + 1, sizeof(output) - output_bytes);
+}
+
+TEST_F(PrintMessageTest, OutputTooSmall) {
+  assert(sizeof(input) >= kTestMessage1.Size());
+  input_bytes = kTestMessage1.Serialize(input);
+  output_bytes = kTestMessage1String.size();
+  EXPECT_FALSE(PrintMessage(output, &output_bytes, input, &input_bytes,
+                           *kTestMessage1.GetType()));
+}
+
+TEST_F(PrintMessageTest, InputTooSmall) {
+  input_bytes = kTestMessage1.Size() - 1;
+  output_bytes = sizeof(output);
+  EXPECT_FALSE(PrintMessage(output, &output_bytes, input, &input_bytes,
+                           *kTestMessage1.GetType()));
+}
+
+TEST_F(PrintMessageTest, Structure) {
+  assert(sizeof(input) >= kTestStructure1.Size());
+  input_bytes = kTestStructure1.Serialize(input);
+  output_bytes = sizeof(output);
+  ASSERT_TRUE(PrintMessage(output, &output_bytes, input, &input_bytes,
+                           *kTestStructure1.GetType()));
+  EXPECT_EQ(kTestStructure1String, ::std::string(output));
+  EXPECT_EQ(kTestStructure1String.size() + 1, sizeof(output) - output_bytes);
+}
+
 }  // namespace testing
 }  // namespace aos