github.com/balzaczyy/golucene@v0.0.0-20151210033525-d0be9ee89713/test_framework/testcase.go (about)

     1  package test_framework
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/balzaczyy/golucene/core/analysis"
     6  	. "github.com/balzaczyy/golucene/core/codec/spi"
     7  	docu "github.com/balzaczyy/golucene/core/document"
     8  	"github.com/balzaczyy/golucene/core/index"
     9  	"github.com/balzaczyy/golucene/core/search"
    10  	"github.com/balzaczyy/golucene/core/store"
    11  	"github.com/balzaczyy/golucene/core/util"
    12  	ti "github.com/balzaczyy/golucene/test_framework/index"
    13  	ts "github.com/balzaczyy/golucene/test_framework/search"
    14  	. "github.com/balzaczyy/golucene/test_framework/util"
    15  	. "github.com/balzaczyy/gounit"
    16  	"io"
    17  	"log"
    18  	"math"
    19  	"math/rand"
    20  	"os"
    21  	"reflect"
    22  )
    23  
    24  // --------------------------------------------------------------------
    25  // Test groups, system properties and other annotations modifying tests
    26  // --------------------------------------------------------------------
    27  
    28  // -----------------------------------------------------------------
    29  // Truly immutable fields and constants, initialized once and valid
    30  // for all suites ever since.
    31  // -----------------------------------------------------------------
    32  
    33  // Use this constant then creating Analyzers and any other version-dependent
    34  // stuff. NOTE: Change this when developmenet starts for new Lucene version:
    35  var TEST_VERSION_CURRENT = util.VERSION_45
    36  
    37  // Throttling
    38  var TEST_THROTTLING = either(TEST_NIGHTLY, THROTTLING_SOMETIMES, THROTTLING_NEVER).(Throttling)
    39  
    40  func either(flag bool, value, orValue interface{}) interface{} {
    41  	if flag {
    42  		return value
    43  	}
    44  	return orValue
    45  }
    46  
    47  // L300
    48  
    49  // -----------------------------------------------------------------
    50  // Class level (suite) rules.
    51  // -----------------------------------------------------------------
    52  
    53  // Class environment setup rule.
    54  var ClassEnvRule = &TestRuleSetupAndRestoreClassEnv{}
    55  
    56  // -----------------------------------------------------------------
    57  // Test facilities and facades for subclasses.
    58  // -----------------------------------------------------------------
    59  
    60  // Create a new index writer config with random defaults
    61  func NewIndexWriterConfig(v util.Version, a analysis.Analyzer) *index.IndexWriterConfig {
    62  	return newRandomIndexWriteConfig(Random(), v, a)
    63  }
    64  
    65  // Create a new index write config with random defaults using the specified random
    66  func newRandomIndexWriteConfig(r *rand.Rand, v util.Version, a analysis.Analyzer) *index.IndexWriterConfig {
    67  	c := index.NewIndexWriterConfig(v, a)
    68  	c.SetSimilarity(ClassEnvRule.similarity)
    69  	if VERBOSE {
    70  		// Even through TestRuleSetupAndRestoreClassEnv calls
    71  		// infoStream.SetDefault, we do it again here so that the
    72  		// PrintStreamInfoStream.messageID increments so that when there
    73  		// are separate instance of IndexWriter created we see "IW 0",
    74  		// "IW 1", "IW 2", ... instead of just always "IW 0":
    75  		c.SetInfoStream(newThreadNameFixingPrintStreamInfoStream(os.Stdout))
    76  	}
    77  
    78  	if r.Intn(2) == 0 {
    79  		c.SetMergeScheduler(index.NewSerialMergeScheduler())
    80  	} else if Rarely(r) {
    81  		log.Println("Use ConcurrentMergeScheduler")
    82  		maxRoutineCount := NextInt(Random(), 1, 4)
    83  		maxMergeCount := NextInt(Random(), maxRoutineCount, maxRoutineCount+4)
    84  		cms := index.NewConcurrentMergeScheduler()
    85  		cms.SetMaxMergesAndRoutines(maxMergeCount, maxRoutineCount)
    86  		c.SetMergeScheduler(cms)
    87  	}
    88  	if r.Intn(2) == 0 {
    89  		if Rarely(r) {
    90  			log.Println("Use crazy value for buffered docs")
    91  			// crazy value
    92  			c.SetMaxBufferedDocs(NextInt(r, 2, 15))
    93  		} else {
    94  			// reasonable value
    95  			c.SetMaxBufferedDocs(NextInt(r, 16, 1000))
    96  		}
    97  	}
    98  	// Go doesn't need thread-affinity state.
    99  	// if r.Intn(2) == 0 {
   100  	// 	maxNumRoutineState := either(Rarely(r),
   101  	// 		NextInt(r, 5, 20), // crazy value
   102  	// 		NextInt(r, 1, 4))  // reasonable value
   103  
   104  	// 	if Rarely(r) {
   105  	// 		// reandom thread pool
   106  	// 		c.SetIndexerThreadPool(newRandomDocumentsWriterPerThreadPool(maxNumRoutineState, r))
   107  	// 	} else {
   108  	// 		// random thread pool
   109  	// 		c.SetMaxThreadStates(maxNumRoutineState)
   110  	// 	}
   111  	// }
   112  
   113  	c.SetMergePolicy(newMergePolicy(r))
   114  
   115  	if Rarely(r) {
   116  		log.Println("Use SimpleMergedSegmentWarmer")
   117  		c.SetMergedSegmentWarmer(index.NewSimpleMergedSegmentWarmer(c.InfoStream()))
   118  	}
   119  	c.SetUseCompoundFile(r.Intn(2) == 0)
   120  	// c.SetUseCompoundFile(false)
   121  	c.SetReaderPooling(r.Intn(2) == 0)
   122  	c.SetReaderTermsIndexDivisor(NextInt(r, 1, 4))
   123  	return c
   124  }
   125  
   126  func newMergePolicy(r *rand.Rand) index.MergePolicy {
   127  	if Rarely(r) {
   128  		log.Println("Use MockRandomMergePolicy")
   129  		return ti.NewMockRandomMergePolicy(r)
   130  	} else if r.Intn(2) == 0 {
   131  		return newTieredMergePolicy(r)
   132  	} else if r.Intn(5) == 0 {
   133  		return newAlcoholicMergePolicy(r /*, ClassEnvRule.timeZone*/)
   134  	} else {
   135  		return newLogMergePolicy(r)
   136  	}
   137  }
   138  
   139  // L883
   140  func newTieredMergePolicy(r *rand.Rand) *index.TieredMergePolicy {
   141  	tmp := index.NewTieredMergePolicy()
   142  	if Rarely(r) {
   143  		log.Println("Use crazy value for max merge at once")
   144  		tmp.SetMaxMergeAtOnce(NextInt(r, 2, 9))
   145  		tmp.SetMaxMergeAtOnceExplicit(NextInt(r, 2, 9))
   146  	} else {
   147  		tmp.SetMaxMergeAtOnce(NextInt(r, 10, 50))
   148  		tmp.SetMaxMergeAtOnceExplicit(NextInt(r, 10, 50))
   149  	}
   150  	if Rarely(r) {
   151  		log.Println("Use crazy value for max merge segment MB")
   152  		tmp.SetMaxMergedSegmentMB(0.2 + r.Float64()*100)
   153  	} else {
   154  		tmp.SetMaxMergedSegmentMB(r.Float64() * 100)
   155  	}
   156  	tmp.SetFloorSegmentMB(0.2 + r.Float64()*2)
   157  	tmp.SetForceMergeDeletesPctAllowed(0 + r.Float64()*30)
   158  	if Rarely(r) {
   159  		log.Println("Use crazy value for max merge per tire")
   160  		tmp.SetSegmentsPerTier(float64(NextInt(r, 2, 20)))
   161  	} else {
   162  		tmp.SetSegmentsPerTier(float64(NextInt(r, 10, 50)))
   163  	}
   164  	configureRandom(r, tmp)
   165  	tmp.SetReclaimDeletesWeight(r.Float64() * 4)
   166  	return tmp
   167  }
   168  
   169  func newAlcoholicMergePolicy(r *rand.Rand /*, tz TimeZone*/) *ti.AlcoholicMergePolicy {
   170  	return ti.NewAlcoholicMergePolicy(rand.New(rand.NewSource(r.Int63())))
   171  }
   172  
   173  func newLogMergePolicy(r *rand.Rand) *index.LogMergePolicy {
   174  	var logmp *index.LogMergePolicy
   175  	if r.Intn(2) == 0 {
   176  		logmp = index.NewLogDocMergePolicy()
   177  	} else {
   178  		logmp = index.NewLogByteSizeMergePolicy()
   179  	}
   180  	if Rarely(r) {
   181  		log.Println("Use crazy value for merge factor")
   182  		logmp.SetMergeFactor(NextInt(r, 2, 9))
   183  	} else {
   184  		logmp.SetMergeFactor(NextInt(r, 10, 50))
   185  	}
   186  	configureRandom(r, logmp)
   187  	return logmp
   188  }
   189  
   190  func configureRandom(r *rand.Rand, mergePolicy index.MergePolicy) {
   191  	if r.Intn(2) == 0 {
   192  		mergePolicy.SetNoCFSRatio(0.1 + r.Float64()*0.8)
   193  	} else if r.Intn(2) == 0 {
   194  		mergePolicy.SetNoCFSRatio(1.0)
   195  	} else {
   196  		mergePolicy.SetNoCFSRatio(0)
   197  	}
   198  
   199  	if Rarely(r) {
   200  		log.Println("Use crazy value for max CFS segment size MB")
   201  		mergePolicy.SetMaxCFSSegmentSizeMB(0.2 + r.Float64()*2)
   202  	} else {
   203  		mergePolicy.SetMaxCFSSegmentSizeMB(math.Inf(1))
   204  	}
   205  }
   206  
   207  /*
   208  Returns a new Direcotry instance. Use this when the test does not care about
   209  the specific Directory implementation (most tests).
   210  
   211  The Directory is wrapped with BaseDirectoryWrapper. This menas usually it
   212  will be picky, such as ensuring that you properly close it and all open files
   213  in your test. It will emulate some features of Windows, such as not allowing
   214  open files ot be overwritten.
   215  */
   216  func NewDirectory() BaseDirectoryWrapper {
   217  	return newDirectoryWithSeed(Random())
   218  }
   219  
   220  // Returns a new Directory instance, using the specified random.
   221  // See NewDirecotry() for more information
   222  func newDirectoryWithSeed(r *rand.Rand) BaseDirectoryWrapper {
   223  	return wrapDirectory(r, newDirectoryImpl(r, TEST_DIRECTORY), Rarely(r))
   224  }
   225  
   226  func wrapDirectory(random *rand.Rand, directory store.Directory, bare bool) BaseDirectoryWrapper {
   227  	if Rarely(random) {
   228  		log.Println("Use NRTCachingDirectory")
   229  		directory = store.NewNRTCachingDirectory(directory, random.Float64(), random.Float64())
   230  	}
   231  
   232  	if Rarely(random) {
   233  		maxMBPerSec := 10 + 5*(random.Float64()-0.5)
   234  		if VERBOSE {
   235  			log.Printf("LuceneTestCase: will rate limit output IndexOutput to %v MB/sec", maxMBPerSec)
   236  		}
   237  		rateLimitedDirectoryWrapper := store.NewRateLimitedDirectoryWrapper(directory)
   238  		switch random.Intn(10) {
   239  		case 3: // sometimes rate limit on flush
   240  			rateLimitedDirectoryWrapper.SetMaxWriteMBPerSec(maxMBPerSec, store.IO_CONTEXT_TYPE_FLUSH)
   241  		case 2: // sometimes rate limit flush & merge
   242  			rateLimitedDirectoryWrapper.SetMaxWriteMBPerSec(maxMBPerSec, store.IO_CONTEXT_TYPE_FLUSH)
   243  			rateLimitedDirectoryWrapper.SetMaxWriteMBPerSec(maxMBPerSec, store.IO_CONTEXT_TYPE_MERGE)
   244  		default:
   245  			rateLimitedDirectoryWrapper.SetMaxWriteMBPerSec(maxMBPerSec, store.IO_CONTEXT_TYPE_MERGE)
   246  		}
   247  		directory = rateLimitedDirectoryWrapper
   248  	}
   249  
   250  	if bare {
   251  		base := NewBaseDirectoryWrapper(directory)
   252  		CloseAfterSuite(NewCloseableDirectory(base, SuiteFailureMarker))
   253  		return base
   254  	} else {
   255  		mock := NewMockDirectoryWrapper(random, directory)
   256  
   257  		mock.SetThrottling(TEST_THROTTLING)
   258  		CloseAfterSuite(NewCloseableDirectory(mock, SuiteFailureMarker))
   259  		return mock
   260  	}
   261  }
   262  
   263  // L1064
   264  func NewTextField(name, value string, stored bool) *docu.Field {
   265  	flag := docu.TEXT_FIELD_TYPE_STORED
   266  	if !stored {
   267  		flag = docu.TEXT_FIELD_TYPE_NOT_STORED
   268  	}
   269  	return NewField(Random(), name, value, flag)
   270  }
   271  
   272  func NewField(r *rand.Rand, name, value string, typ *docu.FieldType) *docu.Field {
   273  	panic("not implemented yet")
   274  	// if Usually(r) || !typ.Indexed() {
   275  	// 	// most of the time, don't modify the params
   276  	// 	return docu.NewStringField(name, value, typ)
   277  	// }
   278  
   279  	// newType := docu.NewFieldTypeFrom(typ)
   280  	// if !newType.Stored() && r.Intn(2) == 0 {
   281  	// 	newType.SetStored(true) // randonly store it
   282  	// }
   283  
   284  	// if !newType.StoreTermVectors() && r.Intn(2) == 0 {
   285  	// 	newType.SetStoreTermVectors(true)
   286  	// 	if !newType.StoreTermVectorOffsets() {
   287  	// 		newType.SetStoreTermVectorOffsets(r.Intn(2) == 0)
   288  	// 	}
   289  	// 	if !newType.StoreTermVectorPositions() {
   290  	// 		newType.SetStoreTermVectorPositions(r.Intn(2) == 0)
   291  
   292  	// 		if newType.StoreTermVectorPositions() && !newType.StoreTermVectorPayloads() && !PREFLEX_IMPERSONATION_IS_ACTIVE {
   293  	// 			newType.SetStoreTermVectorPayloads(r.Intn(2) == 2)
   294  	// 		}
   295  	// 	}
   296  	// }
   297  
   298  	// return docu.NewStringField(name, value, newType)
   299  }
   300  
   301  // Ian: Different from Lucene's default random class initializer, I have to
   302  // explicitly initialize different directory randomly.
   303  func newDirectoryImpl(random *rand.Rand, clazzName string) store.Directory {
   304  	if clazzName == "random" {
   305  		if Rarely(random) {
   306  			switch random.Intn(1) {
   307  			case 0:
   308  				clazzName = "SimpleFSDirectory"
   309  			}
   310  		} else {
   311  			clazzName = "RAMDirectory"
   312  		}
   313  	}
   314  	if clazzName == "RAMDirectory" {
   315  		return store.NewRAMDirectory()
   316  	} else {
   317  		path := TempDir("index")
   318  		if err := os.MkdirAll(path, os.ModeTemporary); err != nil {
   319  			panic(err)
   320  		}
   321  		switch clazzName {
   322  		case "SimpleFSDirectory":
   323  			d, err := store.NewSimpleFSDirectory(path)
   324  			if err != nil {
   325  				panic(err)
   326  			}
   327  			return d
   328  		}
   329  		panic(fmt.Sprintf("not supported yet: %v", clazzName))
   330  	}
   331  }
   332  
   333  func NewDefaultIOContext(r *rand.Rand) store.IOContext {
   334  	return NewIOContext(r, store.IO_CONTEXT_DEFAULT)
   335  }
   336  
   337  func NewIOContext(r *rand.Rand, oldContext store.IOContext) store.IOContext {
   338  	randomNumDocs := r.Intn(4192)
   339  	size := r.Int63n(512) * int64(randomNumDocs)
   340  	if oldContext.FlushInfo != nil {
   341  		// Always return at least the estimatedSegmentSize of the
   342  		// incoming IOContext:
   343  		if size < oldContext.FlushInfo.EstimatedSegmentSize {
   344  			size = oldContext.FlushInfo.EstimatedSegmentSize
   345  		}
   346  		return store.NewIOContextForFlush(&store.FlushInfo{randomNumDocs, size})
   347  	} else if oldContext.MergeInfo != nil {
   348  		// Always return at least the estimatedMergeBytes of the
   349  		// incoming IOContext:
   350  		if size < oldContext.MergeInfo.EstimatedMergeBytes {
   351  			size = oldContext.MergeInfo.EstimatedMergeBytes
   352  		}
   353  		return store.NewIOContextForMerge(
   354  			&store.MergeInfo{randomNumDocs, size, r.Intn(2) == 0, NextInt(r, 1, 100)})
   355  	} else {
   356  		// Make a totally random IOContext:
   357  		switch r.Intn(5) {
   358  		case 1:
   359  			return store.IO_CONTEXT_READ
   360  		case 2:
   361  			return store.IO_CONTEXT_READONCE
   362  		case 3:
   363  			return store.NewIOContextForMerge(&store.MergeInfo{randomNumDocs, size, true, -1})
   364  		case 4:
   365  			return store.NewIOContextForFlush(&store.FlushInfo{randomNumDocs, size})
   366  		default:
   367  			return store.IO_CONTEXT_DEFAULT
   368  		}
   369  	}
   370  }
   371  
   372  // L1193
   373  /*
   374  Sometimes wrap the IndexReader as slow, parallel or filter reader (or
   375  combinations of that)
   376  */
   377  func maybeWrapReader(r index.IndexReader) (index.IndexReader, error) {
   378  	random := Random()
   379  	if Rarely(random) {
   380  		panic("not implemented yet")
   381  	}
   382  	return r, nil
   383  }
   384  
   385  // L1305
   386  // Create a new searcher over the reader. This searcher might randomly use threads
   387  func NewSearcher(r index.IndexReader) *ts.AssertingIndexSearcher {
   388  	return newSearcher(r, true)
   389  }
   390  
   391  /*
   392  Create a new searcher over the reader. This searcher might randomly
   393  use threads. If maybeWrap is true, this searcher migt wrap the reader
   394  with one that return nil for sequentialSubReaders.
   395  */
   396  func newSearcher(r index.IndexReader, maybeWrap bool) *ts.AssertingIndexSearcher {
   397  	random := Random()
   398  	var err error
   399  	// By default, GoLucene would make use of Goroutines to do
   400  	// concurrent search and collect
   401  	//
   402  	// if util.Usually(random) {
   403  	if maybeWrap {
   404  		r, err = maybeWrapReader(r)
   405  		assert(err == nil)
   406  	}
   407  	if random.Intn(2) == 0 {
   408  		ss := ts.NewAssertingIndexSearcher(random, r)
   409  		ss.SetSimilarity(ClassEnvRule.similarity)
   410  		return ss
   411  	}
   412  	ss := ts.NewAssertingIndexSearcherFromContext(random, r.Context())
   413  	ss.SetSimilarity(ClassEnvRule.similarity)
   414  	return ss
   415  	// }
   416  }
   417  
   418  // util/TestRuleSetupAndRestoreClassEnv.java
   419  
   420  var suppressedCodecs string
   421  
   422  func SuppressCodecs(name string) {
   423  	suppressedCodecs = name
   424  }
   425  
   426  type ThreadNameFixingPrintStreamInfoStream struct {
   427  	*util.PrintStreamInfoStream
   428  }
   429  
   430  func newThreadNameFixingPrintStreamInfoStream(w io.Writer) *ThreadNameFixingPrintStreamInfoStream {
   431  	return &ThreadNameFixingPrintStreamInfoStream{util.NewPrintStreamInfoStream(w)}
   432  }
   433  
   434  func (is *ThreadNameFixingPrintStreamInfoStream) Message(component, message string, args ...interface{}) {
   435  	if "TP" == component {
   436  		return // ignore test points!
   437  	}
   438  	is.PrintStreamInfoStream.Message(component, message, args...)
   439  }
   440  
   441  /*
   442  Setup and restore suite-level environment (fine grained junk that
   443  doesn't fit anywhere else)
   444  */
   445  type TestRuleSetupAndRestoreClassEnv struct {
   446  	savedCodec      Codec
   447  	savedInfoStream util.InfoStream
   448  
   449  	similarity search.Similarity
   450  	codec      Codec
   451  
   452  	avoidCodecs map[string]bool
   453  }
   454  
   455  func (rule *TestRuleSetupAndRestoreClassEnv) Before() error {
   456  	// if verbose: print some debugging stuff about which codecs are loaded.
   457  	if VERBOSE {
   458  		for _, codec := range AvailableCodecs() {
   459  			log.Printf("Loaded codec: '%v': %v", codec,
   460  				reflect.TypeOf(LoadCodec(codec)))
   461  		}
   462  
   463  		for _, postingFormat := range AvailablePostingsFormats() {
   464  			log.Printf("Loaded postingsFormat: '%v': %v", postingFormat,
   465  				reflect.TypeOf(LoadPostingsFormat(postingFormat)))
   466  		}
   467  	}
   468  
   469  	rule.savedInfoStream = util.DefaultInfoStream()
   470  	random := Random()
   471  	if INFOSTREAM {
   472  		util.SetDefaultInfoStream(newThreadNameFixingPrintStreamInfoStream(os.Stdout))
   473  	} else if random.Intn(2) == 0 {
   474  		util.SetDefaultInfoStream(NewNullInfoStream())
   475  	}
   476  
   477  	rule.avoidCodecs = make(map[string]bool)
   478  	if suppressedCodecs != "" {
   479  		rule.avoidCodecs[suppressedCodecs] = true
   480  	}
   481  
   482  	rule.savedCodec = DefaultCodec()
   483  	randomVal := random.Intn(10)
   484  	if "Lucene3x" == TEST_CODEC ||
   485  		"random" == TEST_CODEC &&
   486  			"random" == TEST_POSTINGSFORMAT &&
   487  			"random" == TEST_DOCVALUESFORMAT &&
   488  			randomVal == 3 &&
   489  			!rule.shouldAvoidCodec("Lucene3x") { // preflex-only setup
   490  		panic("not supported yet")
   491  	} else if "Lucene40" == TEST_CODEC ||
   492  		"random" == TEST_CODEC &&
   493  			"random" == TEST_POSTINGSFORMAT &&
   494  			randomVal == 0 &&
   495  			!rule.shouldAvoidCodec("Lucene40") { // 4.0 setup
   496  		panic("not supported yet")
   497  	} else if "Lucene41" == TEST_CODEC ||
   498  		"random" == TEST_CODEC &&
   499  			"random" == TEST_POSTINGSFORMAT &&
   500  			"random" == TEST_DOCVALUESFORMAT &&
   501  			randomVal == 1 &&
   502  			!rule.shouldAvoidCodec("Lucene41") {
   503  		panic("not supported yet")
   504  	} else if "Lucene42" == TEST_CODEC ||
   505  		"random" == TEST_CODEC &&
   506  			"random" == TEST_POSTINGSFORMAT &&
   507  			"random" == TEST_DOCVALUESFORMAT &&
   508  			randomVal == 2 &&
   509  			!rule.shouldAvoidCodec("Lucene42") {
   510  		panic("not supported yet")
   511  	} else if "Lucene45" == TEST_CODEC ||
   512  		"random" == TEST_CODEC &&
   513  			"random" == TEST_POSTINGSFORMAT &&
   514  			"random" == TEST_DOCVALUESFORMAT &&
   515  			randomVal == 3 &&
   516  			!rule.shouldAvoidCodec("Lucene45") {
   517  		panic("not supported yet")
   518  	} else if "Lucene46" == TEST_CODEC ||
   519  		"random" == TEST_CODEC &&
   520  			"random" == TEST_POSTINGSFORMAT &&
   521  			"random" == TEST_DOCVALUESFORMAT &&
   522  			randomVal == 4 &&
   523  			!rule.shouldAvoidCodec("Lucene46") {
   524  		panic("not supported yet")
   525  	} else if "Lucene49" == TEST_CODEC ||
   526  		"random" == TEST_CODEC &&
   527  			"random" == TEST_POSTINGSFORMAT &&
   528  			"random" == TEST_DOCVALUESFORMAT &&
   529  			randomVal == 5 &&
   530  			!rule.shouldAvoidCodec("Lucene49") {
   531  
   532  		rule.codec = LoadCodec("Lucene49")
   533  		OLD_FORMAT_IMPERSONATION_IS_ACTIVE = true
   534  
   535  	} else if "random" != TEST_POSTINGSFORMAT ||
   536  		"random" != TEST_DOCVALUESFORMAT {
   537  		// the user wired postings or DV: this is messy
   538  		// refactor into RandomCodec...
   539  
   540  		panic("not supported yet")
   541  	} else if "SimpleText" == TEST_CODEC ||
   542  		"random" == TEST_CODEC &&
   543  			randomVal == 9 &&
   544  			Rarely(random) &&
   545  			!rule.shouldAvoidCodec("SimpleText") {
   546  		panic("not supported yet")
   547  	} else if "Appending" == TEST_CODEC ||
   548  		"random" == TEST_CODEC &&
   549  			randomVal == 8 &&
   550  			!rule.shouldAvoidCodec("Appending") {
   551  		panic("not supported yet")
   552  	} else if "CheapBastard" == TEST_CODEC ||
   553  		"random" == TEST_CODEC &&
   554  			randomVal == 8 &&
   555  			!rule.shouldAvoidCodec("CheapBastard") &&
   556  			!rule.shouldAvoidCodec("Lucene41") {
   557  		panic("not supported yet")
   558  	} else if "Asserting" == TEST_CODEC ||
   559  		"random" == TEST_CODEC &&
   560  			randomVal == 6 &&
   561  			!rule.shouldAvoidCodec("Asserting") {
   562  		panic("not implemented yet")
   563  	} else if "Compressing" == TEST_CODEC ||
   564  		"random" == TEST_CODEC &&
   565  			randomVal == 5 &&
   566  			!rule.shouldAvoidCodec("Compressing") {
   567  		panic("not supported yet")
   568  	} else if "random" != TEST_CODEC {
   569  		rule.codec = LoadCodec(TEST_CODEC)
   570  	} else if "random" == TEST_POSTINGSFORMAT {
   571  		panic("not supported yet")
   572  	} else {
   573  		panic("should not be here")
   574  	}
   575  	log.Printf("Use codec: %v", rule.codec)
   576  	DefaultCodec = func() Codec { return rule.codec }
   577  
   578  	// Initialize locale/ timezone
   579  	// testLocale := or(os.Getenv("tests.locale"), "random")
   580  	// testTimeZon := or(os.Getenv("tests.timezone"), "random")
   581  
   582  	// Always pick a random one for consistency (whether tests.locale
   583  	// was specified or not)
   584  	// Ian: it's not supported yet
   585  	// rule.savedLocale := DefaultLocale()
   586  	// if "random" == testLocale {
   587  	// 	rule.locale = randomLocale(random)
   588  	// } else {
   589  	// 	rule.locale = localeForName(testLocale)
   590  	// }
   591  	// SetDefaultLocale(rule.locale)
   592  
   593  	// SetDefaultTimeZone() will set user.timezone to the default
   594  	// timezone of the user's locale. So store the original property
   595  	// value and restore it at end.
   596  	// rule.restoreProperties["user.timezone"] = os.Getenv("user.timezone")
   597  	// rule.savedTimeZone = DefaultTimeZone()
   598  	// if "random" == testTimeZone {
   599  	// 	rule.timeZone = randomTimeZone(random)
   600  	// } else {
   601  	// 	rule.timeZone = TimeZone(testTimeZone)
   602  	// }
   603  	// SetDefaultTImeZone(rule.timeZone)
   604  
   605  	if random.Intn(2) == 0 {
   606  		rule.similarity = search.NewDefaultSimilarity()
   607  	} else {
   608  		rule.similarity = ts.NewRandomSimilarityProvider(random)
   609  	}
   610  
   611  	// Check codec restrictions once at class level.
   612  	err := rule.checkCodecRestrictions(rule.codec)
   613  	if err != nil {
   614  		log.Printf("NOTE: %v Suppressed codecs: %v", err, rule.avoidCodecs)
   615  		return err
   616  	}
   617  
   618  	// We have "stickiness" so taht sometimes all we do is vary the RAM
   619  	// buffer size, other times just the doc count to flush by, else
   620  	// both. This way the assertMemory in DWFC sometimes runs (when we
   621  	// always flush by RAM).
   622  	// setLiveIWCFlushMode(LiveIWCFlushMode(random.Intn(3)))
   623  
   624  	return nil
   625  }
   626  
   627  func or(a, b string) string {
   628  	if len(a) > 0 {
   629  		return a
   630  	}
   631  	return b
   632  }
   633  
   634  // Check codec restrictions.
   635  func (rule *TestRuleSetupAndRestoreClassEnv) checkCodecRestrictions(codec Codec) error {
   636  	assert(codec != nil)
   637  	AssumeTrue(fmt.Sprintf("Class not allowed to use codec: %v.", codec.Name),
   638  		rule.shouldAvoidCodec(codec.Name()))
   639  
   640  	if _, ok := codec.(*index.RandomCodec); ok && len(rule.avoidCodecs) > 0 {
   641  		panic("not implemented yet")
   642  	}
   643  
   644  	pf := codec.PostingsFormat()
   645  	AssumeFalse(fmt.Sprintf("Class not allowed to use postings format: %v.", pf.Name()),
   646  		rule.shouldAvoidCodec(pf.Name()))
   647  
   648  	AssumeFalse(fmt.Sprintf("Class not allowed to use postings format: %v.", TEST_POSTINGSFORMAT),
   649  		rule.shouldAvoidCodec(TEST_POSTINGSFORMAT))
   650  
   651  	return nil
   652  }
   653  
   654  func (rule *TestRuleSetupAndRestoreClassEnv) After() error {
   655  	panic("not implemented yet")
   656  	// savedCodec := rule.savedCodec
   657  	// index.DefaultCodec = func() Codec { return savedCodec }
   658  	// util.SetDefaultInfoStream(rule.savedInfoStream)
   659  	// // if rule.savedLocale != nil {
   660  	// // 	SetDefaultLocale(rule.savedLocale)
   661  	// // }
   662  	// // if rule.savedTimeZone != nil {
   663  	// // 	SetDefaultTimeZone(rule.savedTimeZone)
   664  	// // }
   665  	// return nil
   666  }
   667  
   668  // Should a given codec be avoided for the currently executing suite?
   669  func (rule *TestRuleSetupAndRestoreClassEnv) shouldAvoidCodec(codec string) bool {
   670  	if len(rule.avoidCodecs) == 0 {
   671  		return false
   672  	}
   673  	_, ok := rule.avoidCodecs[codec]
   674  	return ok
   675  }