Index: lucene/core/src/test/org/apache/lucene/index/TestBinaryTerms.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestBinaryTerms.java	(revision 1468127)
+++ lucene/core/src/test/org/apache/lucene/index/TestBinaryTerms.java	(working copy)
@@ -69,4 +69,9 @@
     ir.close();
     dir.close();
   }
+  
+  public void testToString() {
+    Term term = new Term("foo", new BytesRef(new byte[] { (byte) 0xff, (byte) 0xfe }));
+    assertEquals("foo:[ff fe]", term.toString());
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/search/TermRangeQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/TermRangeQuery.java	(revision 1468127)
+++ lucene/core/src/java/org/apache/lucene/search/TermRangeQuery.java	(working copy)
@@ -19,6 +19,7 @@
 
 import java.io.IOException;
 
+import org.apache.lucene.index.Term;
 import org.apache.lucene.index.Terms;
 import org.apache.lucene.index.TermsEnum;
 import org.apache.lucene.util.AttributeSource;
@@ -122,9 +123,9 @@
       }
       buffer.append(includeLower ? '[' : '{');
       // TODO: all these toStrings for queries should just output the bytes, it might not be UTF-8!
-      buffer.append(lowerTerm != null ? ("*".equals(lowerTerm.utf8ToString()) ? "\\*" : lowerTerm.utf8ToString())  : "*");
+      buffer.append(lowerTerm != null ? ("*".equals(Term.toString(lowerTerm)) ? "\\*" : Term.toString(lowerTerm))  : "*");
       buffer.append(" TO ");
-      buffer.append(upperTerm != null ? ("*".equals(upperTerm.utf8ToString()) ? "\\*" : upperTerm.utf8ToString()) : "*");
+      buffer.append(upperTerm != null ? ("*".equals(Term.toString(upperTerm)) ? "\\*" : Term.toString(upperTerm)) : "*");
       buffer.append(includeUpper ? ']' : '}');
       buffer.append(ToStringUtils.boost(getBoost()));
       return buffer.toString();
Index: lucene/core/src/java/org/apache/lucene/index/Term.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/Term.java	(revision 1468127)
+++ lucene/core/src/java/org/apache/lucene/index/Term.java	(working copy)
@@ -17,7 +17,13 @@
  * limitations under the License.
  */
 
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+
 import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.IOUtils;
 
 /**
   A Term represents a word from text.  This is the unit of search.  It is
@@ -69,7 +75,23 @@
   /** Returns the text of this term.  In the case of words, this is simply the
     text of the word.  In the case of dates and other types, this is an
     encoding of the object as a string.  */
-  public final String text() { return bytes.utf8ToString(); }
+  public final String text() { 
+    return toString(bytes);
+  }
+  
+  /** Returns human-readable form of the term text. If the term is not unicode,
+   * the raw bytes will be printed instead. */
+  public static final String toString(BytesRef termText) {
+    // the term might not be text, but usually is. so we make a best effort
+    CharsetDecoder decoder = IOUtils.CHARSET_UTF_8.newDecoder()
+        .onMalformedInput(CodingErrorAction.REPORT)
+        .onUnmappableCharacter(CodingErrorAction.REPORT);
+    try {
+      return decoder.decode(ByteBuffer.wrap(termText.bytes, termText.offset, termText.length)).toString();
+    } catch (CharacterCodingException e) {
+      return termText.toString();
+    }
+  }
 
   /** Returns the bytes of this term. */
   public final BytesRef bytes() { return bytes; }
@@ -132,5 +154,5 @@
   }
 
   @Override
-  public final String toString() { return field + ":" + bytes.utf8ToString(); }
+  public final String toString() { return field + ":" + text(); }
 }
Index: lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java
===================================================================
--- lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java	(revision 1468127)
+++ lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java	(working copy)
@@ -43,6 +43,7 @@
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.MatchAllDocsQuery;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryUtils;
 import org.apache.lucene.search.ScoreDoc;
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.TopDocs;
@@ -148,6 +149,7 @@
     // Making sure the query yields 25 documents with the facet "a"
     DrillDownQuery q = new DrillDownQuery(defaultParams);
     q.add(new CategoryPath("a"));
+    QueryUtils.check(q);
     TopDocs docs = searcher.search(q, 100);
     assertEquals(25, docs.totalHits);
     
Index: lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java	(revision 1468127)
+++ lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java	(working copy)
@@ -181,7 +181,9 @@
   
   @Override
   public int hashCode() {
-    return query.hashCode();
+    final int prime = 31;
+    int result = super.hashCode();
+    return prime * result + query.hashCode();
   }
   
   @Override
@@ -191,7 +193,7 @@
     }
     
     DrillDownQuery other = (DrillDownQuery) obj;
-    return query.equals(other.query);
+    return query.equals(other.query) && getBoost() == other.getBoost();
   }
   
   @Override
Index: lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java	(revision 1468127)
+++ lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java	(working copy)
@@ -65,6 +65,8 @@
   /** Ensures, that the returned {@code Weight} is not normalized again, which may produce wrong scores. */
   @Override
   public Weight createNormalizedWeight(Query query) throws IOException {
+    // TODO: use the more sophisticated QueryUtils.check sometimes!
+    QueryUtils.check(query);
     final Weight w = super.createNormalizedWeight(query);
     return new AssertingWeight(random, w) {
 
